/* libp11, a simple layer on top of PKCS#11 API
 * Copyright (C) 2005 Olaf Kirch <okir@lst.de>
 * Copyright (C) 2016-2025 Michał Trojnara <Michal.Trojnara@stunnel.org>
 * Copyright © 2025 Mobi - Com Polska Sp. z o.o.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
 */

#include "libp11-int.h"
#include <string.h>
#include <openssl/ui.h>
#include <openssl/bn.h>

/* The maximum length of PIN */
#define MAX_PIN_LENGTH   256

#if OPENSSL_VERSION_NUMBER >= 0x30000000L
static int evp_pkey_ex_index = 0;
#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */

#if OPENSSL_VERSION_NUMBER < 0x40000000L
# if OPENSSL_VERSION_NUMBER >= 0x30000000L
static int pkey_ex_index = 0;
# endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L*/

#if OPENSSL_VERSION_NUMBER < 0x10101000L || defined(LIBRESSL_VERSION_NUMBER)
static EVP_PKEY_METHOD *orig_method_rsa = NULL;
#else
static const EVP_PKEY_METHOD *orig_method_rsa = NULL;
#endif /* OPENSSL_VERSION_NUMBER < 0x10101000L || defined(LIBRESSL_VERSION_NUMBER) */

static int (*orig_pkey_rsa_sign_init) (EVP_PKEY_CTX *ctx);
static int (*orig_pkey_rsa_sign) (EVP_PKEY_CTX *ctx,
	unsigned char *sig, size_t *siglen,
	const unsigned char *tbs, size_t tbslen);
static int (*orig_pkey_rsa_decrypt_init) (EVP_PKEY_CTX *ctx);
static int (*orig_pkey_rsa_decrypt) (EVP_PKEY_CTX *ctx,
	unsigned char *out, size_t *outlen,
	const unsigned char *in, size_t inlen);
#endif /* OPENSSL_VERSION_NUMBER < 0x40000000L */

#if !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L
/* DER OIDs */
static const unsigned char OID_ED25519[] = { 0x06, 0x03, 0x2B, 0x65, 0x70 };
static const unsigned char OID_ED448[]   = { 0x06, 0x03, 0x2B, 0x65, 0x71 };
static const unsigned char OID_X25519[]  = { 0x06, 0x03, 0x2b, 0x65, 0x6e };
static const unsigned char OID_X448[]    = { 0x06, 0x03, 0x2b, 0x65, 0x6f };

/* PrintableString forms used by some tokens (e.g. SoftHSM) */
static const unsigned char STR_ED25519[] = {
	0x13, 0x0C, /* tag + length */
	'e','d','w','a','r','d','s','2','5','5','1','9'
};
static const unsigned char STR_ED448[] = {
	0x13, 0x0A, /* tag + length */
	'e','d','w','a','r','d','s','4','4','8'
};

static const unsigned char STR_X25519[] = {
	0x13, 0x0B, /* tag + length */
	'c','u','r','v','e','2','5','5','1','9'
};

static const unsigned char STR_X448[] = {
	0x13, 0x09, /* tag + length */
	'c','u','r','v','e','4','4','8'
};

#endif /* !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L */

#if OPENSSL_VERSION_NUMBER < 0x100020d0L || defined(LIBRESSL_VERSION_NUMBER)
struct evp_pkey_method_st {
    int pkey_id;
    int flags;
    int (*init) (EVP_PKEY_CTX *ctx);
    int (*copy) (EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src);
    void (*cleanup) (EVP_PKEY_CTX *ctx);
    int (*paramgen_init) (EVP_PKEY_CTX *ctx);
    int (*paramgen) (EVP_PKEY_CTX *ctx, EVP_PKEY *pkey);
    int (*keygen_init) (EVP_PKEY_CTX *ctx);
    int (*keygen) (EVP_PKEY_CTX *ctx, EVP_PKEY *pkey);
    int (*sign_init) (EVP_PKEY_CTX *ctx);
    int (*sign) (EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen,
                 const unsigned char *tbs, size_t tbslen);
    int (*verify_init) (EVP_PKEY_CTX *ctx);
    int (*verify) (EVP_PKEY_CTX *ctx,
                   const unsigned char *sig, size_t siglen,
                   const unsigned char *tbs, size_t tbslen);
    int (*verify_recover_init) (EVP_PKEY_CTX *ctx);
    int (*verify_recover) (EVP_PKEY_CTX *ctx,
                           unsigned char *rout, size_t *routlen,
                           const unsigned char *sig, size_t siglen);
    int (*signctx_init) (EVP_PKEY_CTX *ctx, EVP_MD_CTX *mctx);
    int (*signctx) (EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen,
                    EVP_MD_CTX *mctx);
    int (*verifyctx_init) (EVP_PKEY_CTX *ctx, EVP_MD_CTX *mctx);
    int (*verifyctx) (EVP_PKEY_CTX *ctx, const unsigned char *sig, int siglen,
                      EVP_MD_CTX *mctx);
    int (*encrypt_init) (EVP_PKEY_CTX *ctx);
    int (*encrypt) (EVP_PKEY_CTX *ctx, unsigned char *out, size_t *outlen,
                    const unsigned char *in, size_t inlen);
    int (*decrypt_init) (EVP_PKEY_CTX *ctx);
    int (*decrypt) (EVP_PKEY_CTX *ctx, unsigned char *out, size_t *outlen,
                    const unsigned char *in, size_t inlen);
    int (*derive_init) (EVP_PKEY_CTX *ctx);
    int (*derive) (EVP_PKEY_CTX *ctx, unsigned char *key, size_t *keylen);
    int (*ctrl) (EVP_PKEY_CTX *ctx, int type, int p1, void *p2);
    int (*ctrl_str) (EVP_PKEY_CTX *ctx, const char *type, const char *value);
}; /* EVP_PKEY_METHOD */
#endif

static int pkcs11_find_keys(PKCS11_SLOT_private *, CK_SESSION_HANDLE, unsigned int,
	PKCS11_TEMPLATE *);
static int pkcs11_init_key(PKCS11_SLOT_private *, CK_SESSION_HANDLE session,
	CK_OBJECT_HANDLE o, CK_OBJECT_CLASS type, PKCS11_KEY **);
static int pkcs11_init_keygen(PKCS11_SLOT_private *, CK_SESSION_HANDLE *);
static int pkcs11_next_key(PKCS11_CTX_private *ctx, PKCS11_SLOT_private *,
	CK_SESSION_HANDLE session, CK_OBJECT_CLASS type);
static int pkcs11_store_key(PKCS11_SLOT_private *, EVP_PKEY *, CK_OBJECT_CLASS,
	char *, unsigned char *, size_t, PKCS11_KEY **);
static void pkcs11_common_pubkey_attr(PKCS11_TEMPLATE *, const char *,
	const unsigned char *, size_t);
static void pkcs11_common_privkey_attr(PKCS11_TEMPLATE *, const char *,
	const unsigned char *, size_t, const PKCS11_params *);

#if OPENSSL_VERSION_NUMBER >= 0x30500000L
#ifndef OPENSSL_NO_ML_DSA
static void pkcs11_luna_pubkey_attr(PKCS11_TEMPLATE *, const char *,
	const unsigned char *, size_t);
static void pkcs11_luna_privkey_attr(PKCS11_TEMPLATE *, const char *,
	const unsigned char *, size_t, const PKCS11_params *);
static PKCS11_OBJECT_ops *pkcs11_mldsa_ops_from_param(CK_ULONG param_set);
#endif /* OPENSSL_NO_ML_DSA */
#ifndef OPENSSL_NO_SLH_DSA
static PKCS11_OBJECT_ops *pkcs11_slhdsa_ops_from_param(CK_ULONG param_set);
#endif /* OPENSSL_NO_SLH_DSA */
#endif /* OPENSSL_VERSION_NUMBER >= 0x30500000L */

#if OPENSSL_VERSION_NUMBER >= 0x30000000L
static PKCS11_OBJECT_ops *pkcs11_falcon_ops_from_param(CK_ULONG param_set);
#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */

#if OPENSSL_VERSION_NUMBER < 0x30000000L
static int EVP_PKEY_is_a(const EVP_PKEY *pkey, const char *name);
#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */

#if OPENSSL_VERSION_NUMBER >= 0x30000000L
static void pkcs11_set_ex_data_evp_pkey(EVP_PKEY *pkey, PKCS11_KEY *key);
static PKCS11_OBJECT_private *pkcs11_get_ex_data_evp_pkey(const EVP_PKEY *pkey);
static void alloc_evp_pkey_ex_index(void);
#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */

/* Helper to acquire object handle from given template */
static CK_OBJECT_HANDLE pkcs11_handle_from_template(PKCS11_SLOT_private *slot,
	CK_SESSION_HANDLE session, PKCS11_TEMPLATE *tmpl)
{
	PKCS11_CTX_private *ctx = slot->ctx;
	CK_OBJECT_HANDLE object = CK_INVALID_HANDLE;
	CK_ULONG count = 0;
	CK_RV rv;

	rv = CRYPTOKI_call(ctx,
		C_FindObjectsInit(session, tmpl->attrs, tmpl->nattr));
	if (rv == CKR_OK) {
		rv = CRYPTOKI_call(ctx,
			C_FindObjects(session, &object, 1, &count));
		CRYPTOKI_call(ctx, C_FindObjectsFinal(session));
	}
	if (rv == CKR_OK && count == 1)
		return object;

	return CK_INVALID_HANDLE;
}

/* Get object from a handle */
PKCS11_OBJECT_private *pkcs11_object_from_handle(PKCS11_SLOT_private *slot,
		CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object)
{
	PKCS11_CTX_private *ctx = slot->ctx;
	PKCS11_OBJECT_private *obj;
	PKCS11_OBJECT_ops *ops = NULL;
	CK_OBJECT_CLASS object_class = -1;
	CK_KEY_TYPE key_type = -1;
	CK_CERTIFICATE_TYPE cert_type = -1;
	size_t size;
	unsigned char *data;

	if (pkcs11_getattr_val(ctx, session, object, CKA_CLASS,
			(CK_BYTE *) &object_class, sizeof(object_class))) {
		pkcs11_log(ctx, LOG_DEBUG, "Missing CKA_CLASS attribute\n");
		return NULL;
	}

	switch (object_class) {
	case CKO_PUBLIC_KEY:
	case CKO_PRIVATE_KEY:
		if (pkcs11_getattr_val(ctx, session, object, CKA_KEY_TYPE,
				(CK_BYTE *)&key_type, sizeof(key_type))) {
			pkcs11_log(ctx, LOG_DEBUG, "Missing CKA_KEY_TYPE attribute\n");
			return NULL;
		}
		switch (key_type) {
		case CKK_RSA:
			ops = &pkcs11_rsa_ops;
			break;
#ifndef OPENSSL_NO_EC
		case CKK_EC:
			ops = &pkcs11_ec_ops;
			break;
#endif /* OPENSSL_NO_EC */
#if !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L
		case CKK_EC_EDWARDS:
			/* Read the CKA_EC_PARAMS to distinguish Ed25519 vs Ed448 */
			if (pkcs11_getattr_alloc(ctx, session, object,
				CKA_EC_PARAMS, &data, &size)) {
				pkcs11_log(ctx, LOG_DEBUG, "Missing CKA_EC_PARAMS attribute\n");
				return NULL;
			}
			if ((size == sizeof(OID_ED25519) &&
				!memcmp(data, OID_ED25519, sizeof(OID_ED25519))) ||
				(size == sizeof(STR_ED25519) &&
				!memcmp(data, STR_ED25519, sizeof(STR_ED25519)))) {
				ops = &pkcs11_ed25519_ops;
			} else if ((size == sizeof(OID_ED448) &&
				!memcmp(data, OID_ED448, sizeof(OID_ED448))) ||
				(size == sizeof(STR_ED448) &&
				!memcmp(data, STR_ED448, sizeof(STR_ED448)))) {
				ops = &pkcs11_ed448_ops;
			} else {
				pkcs11_log(ctx, LOG_DEBUG, "Unsupported EdDSA OID\n");
				OPENSSL_free(data);
				return NULL;
			}
			OPENSSL_free(data);
			break;
		case CKK_EC_MONTGOMERY:
			/* Read the CKA_EC_PARAMS to distinguish X25519 vs X448 */
			if (pkcs11_getattr_alloc(ctx, session, object,
				CKA_EC_PARAMS, &data, &size)) {
				pkcs11_log(ctx, LOG_DEBUG, "Missing CKA_EC_PARAMS attribute\n");
				return NULL;
			}
			if ((size == sizeof(OID_X25519) &&
				!memcmp(data, OID_X25519, sizeof(OID_X25519))) ||
				(size == sizeof(STR_X25519) &&
				!memcmp(data, STR_X25519, sizeof(STR_X25519)))) {
				ops = &pkcs11_x25519_ops;
			} else if ((size == sizeof(OID_X448) &&
				!memcmp(data, OID_X448, sizeof(OID_X448))) ||
				(size == sizeof(STR_X448) &&
				!memcmp(data, STR_X448, sizeof(STR_X448)))) {
				ops = &pkcs11_x448_ops;
			} else {
				pkcs11_log(ctx, LOG_DEBUG, "Unsupported XDH OID\n");
				OPENSSL_free(data);
				return NULL;
			}
			OPENSSL_free(data);
			break;
#endif /* !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L */

#if OPENSSL_VERSION_NUMBER >= 0x30500000L
#ifndef OPENSSL_NO_ML_DSA
		case CKK_ML_DSA: {
			CK_ULONG param_set;

			/* Read ML-DSA parameters to distinguish
			 * ML-DSA-44 vs ML-DSA-65 vs ML-DSA-87 */
			if (pkcs11_getattr_alloc(ctx, session, object,
				CKA_PARAMETER_SET, &data, &size)) {
				pkcs11_log(ctx, LOG_DEBUG, "Missing CKA_PARAMETER_SET attribute\n");
				return NULL;
			}
			if (size != sizeof(CK_ULONG)) {
				pkcs11_log(ctx, LOG_DEBUG, "Invalid CKA_PARAMETER_SET size for ML-DSA\n");
				OPENSSL_free(data);
				return NULL;
			}
			memcpy(&param_set, data, sizeof(param_set));
			OPENSSL_free(data);

			ops = pkcs11_mldsa_ops_from_param(param_set);
			if (ops == NULL) {
				pkcs11_log(ctx, LOG_DEBUG, "Unsupported ML-DSA parameter set\n");
				return NULL;
			}
			break;
		}
#endif /* OPENSSL_NO_ML_DSA */
#ifndef OPENSSL_NO_SLH_DSA
		case CKK_SLH_DSA: {
			CK_ULONG param_set;

			/* Read SLH-DSA parameters to distinguish
			 * SHA2/SHAKE and 128/192/256 variants */
			if (pkcs11_getattr_alloc(ctx, session, object,
				CKA_PARAMETER_SET, &data, &size)) {
				pkcs11_log(ctx, LOG_DEBUG, "Missing CKA_PARAMETER_SET attribute\n");
				return NULL;
			}
			if (size != sizeof(CK_ULONG)) {
				pkcs11_log(ctx, LOG_DEBUG, "Invalid CKA_PARAMETER_SET size for SLH-DSA\n");
				OPENSSL_free(data);
				return NULL;
			}
			memcpy(&param_set, data, sizeof(param_set));
			OPENSSL_free(data);

			ops = pkcs11_slhdsa_ops_from_param(param_set);
			if (ops == NULL) {
				pkcs11_log(ctx, LOG_DEBUG, "Unsupported SLH-DSA parameter set\n");
				return NULL;
			}
			break;
		}
#endif /* OPENSSL_NO_SLH_DSA */
#endif /* OPENSSL_VERSION_NUMBER >= 0x30500000L */

#if OPENSSL_VERSION_NUMBER >= 0x30000000L
		case CKK_FALCON: {
			CK_ULONG param_set;

			/* Read PQC FALCON parameters to distinguish
			 * Falcon-512 vs Falcon-1024 */
			if (pkcs11_getattr_alloc(ctx, session, object,
				CKA_PARAMETER_SET, &data, &size)) {
				pkcs11_log(ctx, LOG_DEBUG, "Missing CKA_PARAMETER_SET attribute\n");
				return NULL;
			}
			if (size != sizeof(CK_ULONG)) {
				pkcs11_log(ctx, LOG_DEBUG, "Invalid CKA_PARAMETER_SET size for PQC FALCON\n");
				OPENSSL_free(data);
				return NULL;
			}
			memcpy(&param_set, data, sizeof(param_set));
			OPENSSL_free(data);

			ops = pkcs11_falcon_ops_from_param(param_set);
			if (ops == NULL) {
				pkcs11_log(ctx, LOG_DEBUG, "Unsupported PQC FALCON parameter set\n");
				return NULL;
			}
			break;
		}
#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */

		default:
			/* Ignore any keys we don't understand */
			pkcs11_log(ctx, LOG_DEBUG,
				"Unsupported CKA_KEY_TYPE attribute value: %lu\n",
				(unsigned long)key_type);
			return NULL;
		}
		break;
	case CKO_CERTIFICATE:
		if (pkcs11_getattr_val(ctx, session, object, CKA_CERTIFICATE_TYPE,
				(CK_BYTE *)&cert_type, sizeof(cert_type))) {
			pkcs11_log(ctx, LOG_DEBUG, "Missing CKA_CERTIFICATE_TYPE attribute\n");
			return NULL;
		}
		/* Ignore unknown certificate types */
		if (cert_type != CKC_X_509) {
			pkcs11_log(ctx, LOG_DEBUG,
				"Unsupported CKA_CERTIFICATE_TYPE attribute value: %lu\n",
				(unsigned long)cert_type);
			return NULL;
		}
		break;
	default:
		return NULL;
	}

	obj = OPENSSL_malloc(sizeof(*obj));
	if (!obj)
		return NULL;

	memset(obj, 0, sizeof(*obj));
	obj->refcnt = 1;
	pthread_mutex_init(&obj->lock, 0);
	obj->object_class = object_class;
	obj->object = object;
	obj->slot = pkcs11_slot_ref(slot);
	obj->id_len = sizeof(obj->id);
	if (pkcs11_getattr_var(ctx, session, object, CKA_ID, obj->id, &obj->id_len)) {
		pkcs11_log(ctx, LOG_DEBUG, "Missing CKA_ID attribute\n");
		obj->id_len = 0;
	}
	if (pkcs11_getattr_alloc(ctx, session, object, CKA_LABEL, (CK_BYTE **)&obj->label, NULL))
		pkcs11_log(ctx, LOG_DEBUG, "Missing CKA_LABEL attribute\n");
	obj->ops = ops;
	obj->forkid = get_forkid();
	switch (object_class) {
	case CKO_PRIVATE_KEY:
		if (pkcs11_getattr_val(ctx, session, object, CKA_ALWAYS_AUTHENTICATE,
				&obj->always_authenticate, sizeof(CK_BBOOL))) {
			pkcs11_log(ctx, LOG_DEBUG, "Missing CKA_ALWAYS_AUTHENTICATE attribute\n");
		}
		break;
	case CKO_CERTIFICATE:
		if (pkcs11_getattr_alloc(ctx, session, object, CKA_VALUE, &data, &size)) {
			pkcs11_log(ctx, LOG_DEBUG, "Missing CKA_VALUE attribute\n");
		} else {
			const unsigned char *p = data;

			obj->x509 = d2i_X509(NULL, &p, (long)size);
			OPENSSL_free(data);
		}
		break;
	}
	return obj;
}

/* Get object based on template */
PKCS11_OBJECT_private *pkcs11_object_from_template(PKCS11_SLOT_private *slot,
	CK_SESSION_HANDLE session, PKCS11_TEMPLATE *tmpl)
{
	PKCS11_OBJECT_private *obj = NULL;
	CK_OBJECT_HANDLE object_handle;
	int release = 0;

	if (session == CK_INVALID_HANDLE) {
		if (pkcs11_get_session(slot, 0, &session))
			return NULL;
		release = 1;
	}

	object_handle = pkcs11_handle_from_template(slot, session, tmpl);
	if (object_handle)
		obj = pkcs11_object_from_handle(slot, session, object_handle);

	if (release)
		pkcs11_put_session(slot, session);

	return obj;
}

PKCS11_OBJECT_private *pkcs11_object_from_object(PKCS11_OBJECT_private *obj,
	CK_SESSION_HANDLE session, CK_OBJECT_CLASS object_class)
{
	PKCS11_TEMPLATE tmpl = {0};
	PKCS11_OBJECT_private *ret;

	pkcs11_addattr_var(&tmpl, CKA_CLASS, object_class);
	pkcs11_addattr(&tmpl, CKA_ID, obj->id, obj->id_len);
	ret = pkcs11_object_from_template(obj->slot, session, &tmpl);
	pkcs11_zap_attrs(&tmpl);
	return ret;
}

void pkcs11_object_free(PKCS11_OBJECT_private *obj)
{
	if (!obj)
		return;

	if (pkcs11_atomic_add(&obj->refcnt, -1, &obj->lock) != 0)
		return;
	pkcs11_slot_unref(obj->slot);
	X509_free(obj->x509);
	OPENSSL_free(obj->label);
	pthread_mutex_destroy(&obj->lock);
	OPENSSL_free(obj);
}

/* Set UI method to allow retrieving CKU_CONTEXT_SPECIFIC PINs interactively */
int pkcs11_set_ui_method(PKCS11_CTX_private *ctx,
		UI_METHOD *ui_method, void *ui_user_data)
{
	if (!ctx)
		return -1;
	ctx->ui_method = ui_method;
	ctx->ui_user_data = ui_user_data;
	return 0;
}

/*
 * Find private key matching a certificate
 */
PKCS11_KEY *pkcs11_find_key(PKCS11_OBJECT_private *cert)
{
	PKCS11_KEY *keys, key_template = {0};
	unsigned int n, count;
	key_template.isPrivate = 1;

	key_template.id = cert->id;
	key_template.id_len = cert->id_len;

	if (pkcs11_enumerate_keys(cert->slot, CKO_PRIVATE_KEY, &key_template, &keys, &count))
		return NULL;
	for (n = 0; n < count; n++) {
		PKCS11_OBJECT_private *kpriv = keys[n]._private;
		if (kpriv && cert->id_len == kpriv->id_len
				&& !memcmp(cert->id, kpriv->id, cert->id_len))
			return &keys[n];
	}
	return NULL;
}

/*
 * Reopens the object by refresing the object handle
 */
int pkcs11_reload_object(PKCS11_OBJECT_private *obj)
{
	PKCS11_SLOT_private *slot = obj->slot;
	CK_SESSION_HANDLE session;
	PKCS11_TEMPLATE tmpl = {0};

	if (pkcs11_get_session(slot, 0, &session))
		return -1;

	pkcs11_addattr_var(&tmpl, CKA_CLASS, obj->object_class);
	if (obj->id_len)
		pkcs11_addattr(&tmpl, CKA_ID, obj->id, obj->id_len);
	if (obj->label)
		pkcs11_addattr_s(&tmpl, CKA_LABEL, obj->label);

	obj->object = pkcs11_handle_from_template(slot, session, &tmpl);

	pkcs11_zap_attrs(&tmpl);
	pkcs11_put_session(slot, session);

	if (obj->object == CK_INVALID_HANDLE)
		CRYPTOKI_checkerr(CKR_F_PKCS11_RELOAD_KEY, CKR_OBJECT_HANDLE_INVALID);

	return 0;
}

/**
 * Generate RSA key pair directly on token
 */
int pkcs11_rsa_keygen(PKCS11_SLOT_private *slot, unsigned int bits,
		const char *label, const unsigned char *id, size_t id_len,
		const PKCS11_params *params)
{
	PKCS11_CTX_private *ctx = slot->ctx;
	CK_SESSION_HANDLE session;
	PKCS11_TEMPLATE pubtmpl = {0}, privtmpl = {0};
	CK_MECHANISM mechanism = {
		CKM_RSA_PKCS_KEY_PAIR_GEN, NULL_PTR, 0
	};
	CK_ULONG num_bits = bits;
	CK_BYTE public_exponent[] = { 0x01, 0x00, 0x01 }; /* 65537 (0x10001) */
	CK_OBJECT_HANDLE pub_key_obj, priv_key_obj;
	int rv;

	if (pkcs11_init_keygen(slot, &session))
		return -1;

	/* The following attributes are necessary for RSA encryption and DSA */
	/* pubkey attributes */
	pkcs11_common_pubkey_attr(&pubtmpl, label, id, id_len);
	pkcs11_addattr_bool(&pubtmpl, CKA_ENCRYPT, TRUE);
	pkcs11_addattr_var(&pubtmpl, CKA_MODULUS_BITS, num_bits);
	pkcs11_addattr(&pubtmpl, CKA_PUBLIC_EXPONENT,
		public_exponent, sizeof public_exponent);

	/* privkey attributes */
	pkcs11_common_privkey_attr(&privtmpl, label, id, id_len, params);
	pkcs11_addattr_bool(&privtmpl, CKA_DECRYPT, TRUE);

	/* call the pkcs11 module to create the key pair */
	rv = CRYPTOKI_call(ctx, C_GenerateKeyPair(
		session, &mechanism,
		pubtmpl.attrs, pubtmpl.nattr,
		privtmpl.attrs, privtmpl.nattr,
		&pub_key_obj, &priv_key_obj));

	pkcs11_put_session(slot, session);

	/* zap all memory allocated when building the template */
	pkcs11_zap_attrs(&privtmpl);
	pkcs11_zap_attrs(&pubtmpl);

	CRYPTOKI_checkerr(CKR_F_PKCS11_GENERATE_KEY, rv);

	return 0;
}

#ifndef OPENSSL_NO_EC

/**
 * Generate EC key pair directly on token
 */
int pkcs11_ec_keygen(PKCS11_SLOT_private *slot, const char *curve,
		const char *label, const unsigned char *id, size_t id_len,
		const PKCS11_params *params)
{
	PKCS11_CTX_private *ctx = slot->ctx;
	CK_SESSION_HANDLE session;
	PKCS11_TEMPLATE pubtmpl = {0}, privtmpl = {0};
	CK_MECHANISM mechanism = {
		CKM_EC_KEY_PAIR_GEN, NULL_PTR, 0
	};
	CK_OBJECT_HANDLE pub_key_obj, priv_key_obj;
	int rv;
	unsigned char *ec_params = NULL;
	int ec_params_len = 0;
	unsigned char *tmp = NULL;
	ASN1_OBJECT *curve_obj = NULL;
	int curve_nid = NID_undef;

	if (pkcs11_init_keygen(slot, &session))
		return -1;

	curve_nid = EC_curve_nist2nid(curve);
	if (curve_nid == NID_undef)
		curve_nid = OBJ_sn2nid(curve);
	if (curve_nid == NID_undef)
		curve_nid = OBJ_ln2nid(curve);
	if (curve_nid == NID_undef) {
		pkcs11_put_session(slot, session);
		return -1;
	}
	curve_obj = OBJ_nid2obj(curve_nid);
	if (!curve_obj) {
		pkcs11_put_session(slot, session);
		return -1;
	}
	/* convert to DER format and take just the length */
	ec_params_len = i2d_ASN1_OBJECT(curve_obj, NULL);
	if (ec_params_len < 0) {
		pkcs11_put_session(slot, session);
		return -1;
	}
	ec_params = OPENSSL_malloc(ec_params_len);
	if (!ec_params) {
		pkcs11_put_session(slot, session);
		return -1;
	}
	/**
	 * ec_params points to beginning of DER encoded object. Since we need this
	 * location later and OpenSSL changes it in i2d_ASN1_OBJECT to point to 1 byte
	 * after DER encoded object, we assign the pointer to temporary throw-away
	 * pointer tmp
	 */
	tmp = ec_params;
	if (i2d_ASN1_OBJECT(curve_obj, &tmp) < 0) {
		pkcs11_put_session(slot, session);
		return -1;
	}

	/* The following attributes are necessary for ECDSA and ECDH mechanisms */
	/* pubkey attributes */
	pkcs11_common_pubkey_attr(&pubtmpl, label, id, id_len);
	pkcs11_addattr(&pubtmpl, CKA_EC_PARAMS, ec_params, ec_params_len);

	/* privkey attributes */
	pkcs11_common_privkey_attr(&privtmpl, label, id, id_len, params);
	pkcs11_addattr_bool(&privtmpl, CKA_DERIVE, TRUE);

	/* call the pkcs11 module to create the key pair */
	rv = CRYPTOKI_call(ctx, C_GenerateKeyPair(
			session, &mechanism,
			pubtmpl.attrs, pubtmpl.nattr,
			privtmpl.attrs, privtmpl.nattr,
			&pub_key_obj, &priv_key_obj));

	pkcs11_put_session(slot, session);

	/* zap all memory allocated when building the template */
	pkcs11_zap_attrs(&privtmpl);
	pkcs11_zap_attrs(&pubtmpl);
	memset(ec_params, 0, ec_params_len);
	OPENSSL_free(ec_params);

	CRYPTOKI_checkerr(CKR_F_PKCS11_GENERATE_KEY, rv);
	return 0;
}
#endif /* OPENSSL_NO_EC */

#if !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L
/**
 * Generate EdDSA (Ed25519 / Ed448) key pair directly on token
 */
int pkcs11_eddsa_keygen(PKCS11_SLOT_private *slot,
	int nid, const char *label, const unsigned char *id,
	size_t id_len, const PKCS11_params *params)
{
	PKCS11_CTX_private *ctx = slot->ctx;
	CK_SESSION_HANDLE session;
	PKCS11_TEMPLATE pubtmpl = {0}, privtmpl = {0};
	CK_MECHANISM mechanism = {
		CKM_EC_EDWARDS_KEY_PAIR_GEN, NULL_PTR, 0
	};
	CK_OBJECT_HANDLE pub_key_obj, priv_key_obj;
	int rv;
	unsigned char *eddsa_params = NULL;
	size_t eddsa_params_len = 0;

	if (pkcs11_init_keygen(slot, &session))
		return -1;

	if (nid == NID_ED25519) {
		eddsa_params = (unsigned char *)OID_ED25519;
		eddsa_params_len = sizeof(OID_ED25519);
	} else if (nid == NID_ED448) {
		eddsa_params = (unsigned char *)OID_ED448;
		eddsa_params_len = sizeof(OID_ED448);
	} else {
		pkcs11_put_session(slot, session);
		return -1; /* unsupported */
	}

	/* public key attributes */
	pkcs11_common_pubkey_attr(&pubtmpl, label, id, id_len);
	pkcs11_addattr(&pubtmpl, CKA_EC_PARAMS, eddsa_params, eddsa_params_len);
	pkcs11_addattr_bool(&pubtmpl, CKA_VERIFY, TRUE);

	/* private key attributes */
	pkcs11_common_privkey_attr(&privtmpl, label, id, id_len, params);
	pkcs11_addattr_bool(&privtmpl, CKA_SIGN, TRUE);

	/* generate key pair */
	rv = CRYPTOKI_call(ctx, C_GenerateKeyPair(
		session, &mechanism,
		pubtmpl.attrs, pubtmpl.nattr,
		privtmpl.attrs, privtmpl.nattr,
		&pub_key_obj, &priv_key_obj));

	/* cleanup */
	pkcs11_put_session(slot, session);
	pkcs11_zap_attrs(&privtmpl);
	pkcs11_zap_attrs(&pubtmpl);

	CRYPTOKI_checkerr(CKR_F_PKCS11_GENERATE_KEY, rv);
	return 0;
}

/**
 * Generate XDH (X25519 / X448) key pair directly on token
 */
int pkcs11_xdh_keygen(PKCS11_SLOT_private *slot,
	int nid, const char *label, const unsigned char *id,
	size_t id_len, const PKCS11_params *params)
{
	PKCS11_CTX_private *ctx = slot->ctx;
	CK_SESSION_HANDLE session;
	PKCS11_TEMPLATE pubtmpl = {0}, privtmpl = {0};
	CK_MECHANISM mechanism = {
		CKM_EC_MONTGOMERY_KEY_PAIR_GEN, NULL_PTR, 0
	};
	CK_OBJECT_HANDLE pub_key_obj, priv_key_obj;
	int rv;
	unsigned char *xdh_params = NULL;
	size_t xdh_params_len = 0;

	if (pkcs11_init_keygen(slot, &session))
		return -1;

	if (nid == NID_X25519) {
		xdh_params = (unsigned char *)OID_X25519;
		xdh_params_len = sizeof(OID_X25519);
	} else if (nid == NID_X448) {
		xdh_params = (unsigned char *)OID_X448;
		xdh_params_len = sizeof(OID_X448);
	} else {
		pkcs11_put_session(slot, session);
		return -1; /* unsupported */
	}

	/* public key attributes */
	pkcs11_common_pubkey_attr(&pubtmpl, label, id, id_len);
	pkcs11_addattr(&pubtmpl, CKA_EC_PARAMS, xdh_params, xdh_params_len);
	pkcs11_addattr_bool(&pubtmpl, CKA_DERIVE, TRUE);

	/* private key attributes */
	pkcs11_common_privkey_attr(&privtmpl, label, id, id_len, params);
	pkcs11_addattr_bool(&privtmpl, CKA_DERIVE, TRUE);

	/* generate key pair */
	rv = CRYPTOKI_call(ctx, C_GenerateKeyPair(
		session, &mechanism,
		pubtmpl.attrs, pubtmpl.nattr,
		privtmpl.attrs, privtmpl.nattr,
		&pub_key_obj, &priv_key_obj));

	/* cleanup */
	pkcs11_put_session(slot, session);
	pkcs11_zap_attrs(&privtmpl);
	pkcs11_zap_attrs(&pubtmpl);

	CRYPTOKI_checkerr(CKR_F_PKCS11_GENERATE_KEY, rv);
	return 0;
}
#endif /* !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L */

#if OPENSSL_VERSION_NUMBER >= 0x30500000L
#ifndef OPENSSL_NO_ML_DSA
/**
 * Generate ML-DSA (ML-DSA-44 / ML-DSA-65 / ML-DSA-87) key pair directly on token
 */
int pkcs11_mldsa_keygen(PKCS11_SLOT_private *slot,
	int nid, const char *label, const unsigned char *id,
	size_t id_len, const PKCS11_params *params)
{
	PKCS11_CTX_private *ctx = slot->ctx;
	CK_SESSION_HANDLE session;
	PKCS11_TEMPLATE pubtmpl = {0}, privtmpl = {0};
	CK_MECHANISM mechanism = {
		CKM_ML_DSA_KEY_PAIR_GEN, NULL_PTR, 0
	};
	CK_SIGN_PARAMETER_SET_TYPE signParamSet = 0;
	CK_OBJECT_HANDLE pub_key_obj, priv_key_obj;
	CK_RV rv;

	if (pkcs11_init_keygen(slot, &session))
		return -1;

	switch (nid) {
	case NID_ML_DSA_44:
		signParamSet = CKP_ML_DSA_44;
		break;
	case NID_ML_DSA_65:
		signParamSet = CKP_ML_DSA_65;
		break;
	case NID_ML_DSA_87:
		signParamSet = CKP_ML_DSA_87;
		break;
	default:
		pkcs11_put_session(slot, session);
		return -1; /* unsupported */
	}

	/* public key attributes */
	pkcs11_common_pubkey_attr(&pubtmpl, label, id, id_len);
	pkcs11_addattr(&pubtmpl, CKA_PARAMETER_SET,
		&signParamSet, sizeof(signParamSet));
	pkcs11_addattr_bool(&pubtmpl, CKA_VERIFY, TRUE);

	/* private key attributes */
	pkcs11_common_privkey_attr(&privtmpl, label, id, id_len, params);
	pkcs11_addattr_bool(&privtmpl, CKA_SIGN, TRUE);

	/* generate key pair */
	rv = CRYPTOKI_call(ctx, C_GenerateKeyPair(
		session, (CK_MECHANISM_PTR)&mechanism,
		pubtmpl.attrs, pubtmpl.nattr,
		privtmpl.attrs, privtmpl.nattr,
		&pub_key_obj, &priv_key_obj));

	if (rv != CKR_OK) {
		/* Thales Luna HSM firmware does not support the wrapping
		 * or unwrapping of CK_ML_DSA private key objects.
		 * Retry with these attributes explicitly disabled. */
		pkcs11_zap_attrs(&privtmpl);
		pkcs11_zap_attrs(&pubtmpl);
		memset(&privtmpl, 0, sizeof(privtmpl));
		memset(&pubtmpl, 0, sizeof(pubtmpl));

		pkcs11_luna_pubkey_attr(&pubtmpl, label, id, id_len);
		pkcs11_addattr(&pubtmpl, CKA_PARAMETER_SET,
			&signParamSet, sizeof(signParamSet));

		pkcs11_luna_privkey_attr(&privtmpl, label, id, id_len, params);
		pkcs11_addattr(&privtmpl, CKA_PARAMETER_SET,
			&signParamSet, sizeof(signParamSet));

		pub_key_obj = CK_INVALID_HANDLE;
		priv_key_obj = CK_INVALID_HANDLE;

		rv = CRYPTOKI_call(ctx, C_GenerateKeyPair(
			session, (CK_MECHANISM_PTR)&mechanism,
			pubtmpl.attrs, pubtmpl.nattr,
			privtmpl.attrs, privtmpl.nattr,
			&pub_key_obj, &priv_key_obj));
	}

	/* cleanup */
	pkcs11_put_session(slot, session);
	pkcs11_zap_attrs(&privtmpl);
	pkcs11_zap_attrs(&pubtmpl);

	CRYPTOKI_checkerr(CKR_F_PKCS11_GENERATE_KEY, rv);
	return 0;
}
#endif /* OPENSSL_NO_ML_DSA */
#ifndef OPENSSL_NO_SLH_DSA
/**
 * Generate SLH-DSA key pair directly on token
 */
int pkcs11_slhdsa_keygen(PKCS11_SLOT_private *slot,
	int nid, const char *label, const unsigned char *id,
	size_t id_len, const PKCS11_params *params)
{
	PKCS11_CTX_private *ctx = slot->ctx;
	CK_SESSION_HANDLE session;
	PKCS11_TEMPLATE pubtmpl = {0}, privtmpl = {0};
	CK_MECHANISM mechanism = {
		CKM_SLH_DSA_KEY_PAIR_GEN, NULL_PTR, 0
	};
	CK_SIGN_PARAMETER_SET_TYPE signParamSet = 0;
	CK_OBJECT_HANDLE pub_key_obj, priv_key_obj;
	CK_RV rv;

	if (pkcs11_init_keygen(slot, &session))
		return -1;

	switch (nid) {
	case NID_SLH_DSA_SHA2_128s:
		signParamSet = CKP_SLH_DSA_SHA2_128S;
		break;
	case NID_SLH_DSA_SHAKE_128s:
		signParamSet = CKP_SLH_DSA_SHAKE_128S;
		break;
	case NID_SLH_DSA_SHA2_128f:
		signParamSet = CKP_SLH_DSA_SHA2_128F;
		break;
	case NID_SLH_DSA_SHAKE_128f:
		signParamSet = CKP_SLH_DSA_SHAKE_128F;
		break;
	case NID_SLH_DSA_SHA2_192s:
		signParamSet = CKP_SLH_DSA_SHA2_192S;
		break;
	case NID_SLH_DSA_SHAKE_192s:
		signParamSet = CKP_SLH_DSA_SHAKE_192S;
		break;
	case NID_SLH_DSA_SHA2_192f:
		signParamSet = CKP_SLH_DSA_SHA2_192F;
		break;
	case NID_SLH_DSA_SHAKE_192f:
		signParamSet = CKP_SLH_DSA_SHAKE_192F;
		break;
	case NID_SLH_DSA_SHA2_256s:
		signParamSet = CKP_SLH_DSA_SHA2_256S;
		break;
	case NID_SLH_DSA_SHAKE_256s:
		signParamSet = CKP_SLH_DSA_SHAKE_256S;
		break;
	case NID_SLH_DSA_SHA2_256f:
		signParamSet = CKP_SLH_DSA_SHA2_256F;
		break;
	case NID_SLH_DSA_SHAKE_256f:
		signParamSet = CKP_SLH_DSA_SHAKE_256F;
		break;
	default:
		pkcs11_put_session(slot, session);
		return -1; /* unsupported */
	}

	/* public key attributes */
	pkcs11_common_pubkey_attr(&pubtmpl, label, id, id_len);
	pkcs11_addattr(&pubtmpl, CKA_PARAMETER_SET,
		&signParamSet, sizeof(signParamSet));
	pkcs11_addattr_bool(&pubtmpl, CKA_VERIFY, TRUE);

	/* private key attributes */
	pkcs11_common_privkey_attr(&privtmpl, label, id, id_len, params);
	pkcs11_addattr(&privtmpl, CKA_PARAMETER_SET,
		&signParamSet, sizeof(signParamSet));
	pkcs11_addattr_bool(&privtmpl, CKA_SIGN, TRUE);

	/* generate key pair */
	rv = CRYPTOKI_call(ctx, C_GenerateKeyPair(
		session, (CK_MECHANISM_PTR)&mechanism,
		pubtmpl.attrs, pubtmpl.nattr,
		privtmpl.attrs, privtmpl.nattr,
		&pub_key_obj, &priv_key_obj));

	/* cleanup */
	pkcs11_put_session(slot, session);
	pkcs11_zap_attrs(&privtmpl);
	pkcs11_zap_attrs(&pubtmpl);

	CRYPTOKI_checkerr(CKR_F_PKCS11_GENERATE_KEY, rv);
	return 0;
}
#endif /* OPENSSL_NO_SLH_DSA */
#endif /* OPENSSL_VERSION_NUMBER >= 0x30500000L */

#if OPENSSL_VERSION_NUMBER >= 0x30000000L
/**
 * Generate PQC FALCON (FALCON-512 / FALCON-1024) key pair directly on token
 */
int pkcs11_falcon_keygen(PKCS11_SLOT_private *slot,
	int nid, const char *label, const unsigned char *id,
	size_t id_len, const PKCS11_params *params)
{
	PKCS11_CTX_private *ctx = slot->ctx;
	CK_SESSION_HANDLE session;
	PKCS11_TEMPLATE pubtmpl = {0}, privtmpl = {0};
	/* Luna Token documents Falcon key-pair generation with CKM_PQC_FALCON.
	 * TODO: Verify CKM_FALCON_KEY_PAIR_GEN compatibility. */
	CK_MECHANISM mechanism = {
		CKM_PQC_FALCON, NULL_PTR, 0
	};
	CK_SIGN_PARAMETER_SET_TYPE signParamSet = 0;
	CK_OBJECT_HANDLE pub_key_obj, priv_key_obj;
	CK_RV rv;

	if (pkcs11_init_keygen(slot, &session))
		return -1;

	if (nid == NID_FALCON_512) {
		signParamSet = CKP_FALCON_512;
	} else if (nid == NID_FALCON_1024) {
		signParamSet = CKP_FALCON_1024;
	} else {
		pkcs11_put_session(slot, session);
		return -1; /* unsupported */
	}

	/* public key attributes */
	pkcs11_common_pubkey_attr(&pubtmpl, label, id, id_len);
	pkcs11_addattr(&pubtmpl, CKA_PARAMETER_SET,
		&signParamSet, sizeof(signParamSet));
	pkcs11_addattr_bool(&pubtmpl, CKA_VERIFY, TRUE);

	/* private key attributes */
	pkcs11_common_privkey_attr(&privtmpl, label, id, id_len, params);
	pkcs11_addattr_bool(&privtmpl, CKA_SIGN, TRUE);

	/* generate key pair */
	rv = CRYPTOKI_call(ctx, C_GenerateKeyPair(
		session, (CK_MECHANISM_PTR)&mechanism,
		pubtmpl.attrs, pubtmpl.nattr,
		privtmpl.attrs, privtmpl.nattr,
		&pub_key_obj, &priv_key_obj));

	/* cleanup */
	pkcs11_put_session(slot, session);
	pkcs11_zap_attrs(&privtmpl);
	pkcs11_zap_attrs(&pubtmpl);

	CRYPTOKI_checkerr(CKR_F_PKCS11_GENERATE_KEY, rv);
	return 0;
}
#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */

/*
 * Store a private key on the token
 */
int pkcs11_store_private_key(PKCS11_SLOT_private *slot, EVP_PKEY *pk,
		char *label, unsigned char *id, size_t id_len)
{
	if (pkcs11_store_key(slot, pk, CKO_PRIVATE_KEY, label, id, id_len, NULL))
		return -1;
	return 0;
}

int pkcs11_store_public_key(PKCS11_SLOT_private *slot, EVP_PKEY *pk,
		char *label, unsigned char *id, size_t id_len)
{
	if (pkcs11_store_key(slot, pk, CKO_PUBLIC_KEY, label, id, id_len, NULL))
		return -1;
	return 0;
}

/*
 * Store private key
 */
static int pkcs11_store_key(PKCS11_SLOT_private *slot, EVP_PKEY *pk,
		CK_OBJECT_CLASS type, char *label, unsigned char *id, size_t id_len,
		PKCS11_KEY **ret_key)
{
	PKCS11_CTX_private *ctx = slot->ctx;
	PKCS11_TEMPLATE tmpl = {0};
	CK_SESSION_HANDLE session;
	CK_OBJECT_HANDLE object;
	CK_KEY_TYPE key_type_rsa = CKK_RSA;
	int rv, r = -1;
	const BIGNUM *rsa_n, *rsa_e, *rsa_d, *rsa_p, *rsa_q, *rsa_dmp1, *rsa_dmq1, *rsa_iqmp;

	/* Now build the key attrs */
	pkcs11_addattr_var(&tmpl, CKA_CLASS, type);
	if (label)
		pkcs11_addattr_s(&tmpl, CKA_LABEL, label);
	if (id && id_len)
		pkcs11_addattr(&tmpl, CKA_ID, id, id_len);
	pkcs11_addattr_bool(&tmpl, CKA_TOKEN, TRUE);
	if (type == CKO_PRIVATE_KEY) {
		pkcs11_addattr_bool(&tmpl, CKA_PRIVATE, TRUE);
		pkcs11_addattr_bool(&tmpl, CKA_SENSITIVE, TRUE);
		pkcs11_addattr_bool(&tmpl, CKA_DECRYPT, TRUE);
		pkcs11_addattr_bool(&tmpl, CKA_SIGN, TRUE);
		pkcs11_addattr_bool(&tmpl, CKA_UNWRAP, TRUE);
	} else { /* CKO_PUBLIC_KEY */
		pkcs11_addattr_bool(&tmpl, CKA_ENCRYPT, TRUE);
		pkcs11_addattr_bool(&tmpl, CKA_VERIFY, TRUE);
		pkcs11_addattr_bool(&tmpl, CKA_WRAP, TRUE);
	}
#if OPENSSL_VERSION_NUMBER >= 0x10100003L || ( defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER >= 0x3050000fL )
	if (EVP_PKEY_is_a(pk, "RSA") || EVP_PKEY_is_a(pk, "RSA-PSS")) {
		RSA *rsa = EVP_PKEY_get1_RSA(pk);
		RSA_get0_key(rsa, &rsa_n, &rsa_e, &rsa_d);
		RSA_get0_factors(rsa, &rsa_p, &rsa_q);
		RSA_get0_crt_params(rsa, &rsa_dmp1, &rsa_dmq1, &rsa_iqmp);
		RSA_free(rsa);
#else
	if (pk->type == EVP_PKEY_RSA) {
		RSA *rsa = pk->pkey.rsa;
		rsa_n = rsa->n;
		rsa_e = rsa->e;
		rsa_d = rsa->d;
		rsa_p = rsa->p;
		rsa_q = rsa->q;
		rsa_dmp1 = rsa->dmp1;
		rsa_dmq1 = rsa->dmq1;
		rsa_iqmp = rsa->iqmp;
#endif
		pkcs11_addattr_var(&tmpl, CKA_KEY_TYPE, key_type_rsa);
		pkcs11_addattr_bn(&tmpl, CKA_MODULUS, rsa_n);
		pkcs11_addattr_bn(&tmpl, CKA_PUBLIC_EXPONENT, rsa_e);
		if (type == CKO_PRIVATE_KEY) {
			pkcs11_addattr_bn(&tmpl, CKA_PRIVATE_EXPONENT, rsa_d);
			pkcs11_addattr_bn(&tmpl, CKA_PRIME_1, rsa_p);
			pkcs11_addattr_bn(&tmpl, CKA_PRIME_2, rsa_q);
			if (rsa_dmp1)
				pkcs11_addattr_bn(&tmpl, CKA_EXPONENT_1, rsa_dmp1);
			if (rsa_dmq1)
				pkcs11_addattr_bn(&tmpl, CKA_EXPONENT_2, rsa_dmq1);
			if (rsa_iqmp)
				pkcs11_addattr_bn(&tmpl, CKA_COEFFICIENT, rsa_iqmp);
		}
	} else {
		pkcs11_zap_attrs(&tmpl);
		P11err(P11_F_PKCS11_STORE_KEY, P11_R_NOT_SUPPORTED);
		return -1;
	}

	if (pkcs11_get_session(slot, 1, &session)) {
		pkcs11_zap_attrs(&tmpl);
		return -1;
	}

	/* Now call the pkcs11 module to create the object */
	rv = CRYPTOKI_call(ctx, C_CreateObject(session, tmpl.attrs, tmpl.nattr, &object));

	/* Zap all memory allocated when building the template */
	pkcs11_zap_attrs(&tmpl);

	if (rv == CKR_OK) {
		/* Gobble the key object */
		r = pkcs11_init_key(slot, session, object, type, ret_key);
	}
	pkcs11_put_session(slot, session);

	CRYPTOKI_checkerr(CKR_F_PKCS11_STORE_KEY, rv);
	return r;

}

/*
 * Get the key type
 */
int pkcs11_get_key_type(PKCS11_OBJECT_private *key)
{
	if (key->ops)
		return key->ops->pkey_type;
	return EVP_PKEY_NONE;
}

/*
 * Create an EVP_PKEY OpenSSL object for a given key
 * Returns the key type specified in object_class.
 */
EVP_PKEY *pkcs11_get_key(PKCS11_OBJECT_private *key0, CK_OBJECT_CLASS object_class)
{
	PKCS11_OBJECT_private *key = key0;
	EVP_PKEY *ret = NULL;
	RSA *rsa;
#if OPENSSL_VERSION_NUMBER < 0x30000000L || defined(LIBRESSL_VERSION_NUMBER)
	EC_KEY *ec_key;
#endif

	if (key->object_class != object_class)
		key = pkcs11_object_from_object(key, CK_INVALID_HANDLE, object_class);
	if (!key || !key->ops)
		goto err;
	if (!key->evp_key) {
		key->evp_key = key->ops->get_evp_key(key);
		if (!key->evp_key)
			goto err;
	}
	/* We need a full copy of the EVP_PKEY as it will be modified later.
	 * Using a reference would mean changes to the duplicated EVP_PKEY could
	 * affect the original one.
	 */
	if (EVP_PKEY_is_a(key->evp_key, "RSA") ||
		EVP_PKEY_is_a(key->evp_key, "RSA-PSS")) {
		/* Do not try to duplicate foreign RSA keys */
		rsa = EVP_PKEY_get1_RSA(key->evp_key);
		if (!rsa)
			goto err;
		ret = EVP_PKEY_new();
		if (!ret) {
			RSA_free(rsa);
			goto err;
		}
		if (!EVP_PKEY_assign_RSA(ret, rsa)) {
			RSA_free(rsa);
			EVP_PKEY_free(ret);
			goto err;
		}
		if (key->object_class != CKO_PRIVATE_KEY)
			pkcs11_set_ex_data_rsa(rsa, NULL);

	} else if (EVP_PKEY_is_a(key->evp_key, "EC")) {
#if OPENSSL_VERSION_NUMBER < 0x30000000L || defined(LIBRESSL_VERSION_NUMBER)
		ec_key = EVP_PKEY_get1_EC_KEY(key->evp_key);
		if (!ec_key)
			goto err;
		ret = EVP_PKEY_new();
		if (!ret) {
			EC_KEY_free(ec_key);
			goto err;
		}
		if (!EVP_PKEY_assign_EC_KEY(ret, ec_key)) {
			EC_KEY_free(ec_key);
			EVP_PKEY_free(ret);
			goto err;
		}
		if (key->object_class != CKO_PRIVATE_KEY)
			pkcs11_set_ex_data_ec(ec_key, NULL);
#else
		/* pkcs11_ec_copy() method is only set for private keys,
		 * so public keys do not have a PKCS11_OBJECT reference */
		ret = EVP_PKEY_dup(key->evp_key);
#endif
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
	} else {
		ret = EVP_PKEY_dup(key->evp_key);
#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
	}

	if (!ret) {
		pkcs11_log(key0->slot->ctx, LOG_DEBUG, "Unsupported key type\n");
		goto err;
	}

#if OPENSSL_VERSION_NUMBER >= 0x30000000L
	/* Store the backing PKCS#11 object in EVP_PKEY ex_data. Public key
	 * ex_data is needed as a workaround for FALCON token-side verify. */
	alloc_evp_pkey_ex_index();
	pkcs11_set_ex_data_evp_pkey(ret, key->public);
#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
err:
	if (key != key0)
		pkcs11_object_free(key);

	return ret;
}

#if OPENSSL_VERSION_NUMBER >= 0x30000000L
/* Return the borrowed PKCS#11 object associated with the EVP_PKEY */
PKCS11_OBJECT_private *pkcs11_get_ex_data_object(const EVP_PKEY *pk)
{
	return pkcs11_get_ex_data_evp_pkey(pk);
}
#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */

/*
 * Authenticate a private the key operation if needed
 * This function *only* handles CKU_CONTEXT_SPECIFIC logins.
 */
int pkcs11_authenticate(PKCS11_OBJECT_private *key, CK_SESSION_HANDLE session)
{
	PKCS11_SLOT_private *slot = key->slot;
	PKCS11_CTX_private *ctx = slot->ctx;
	char pin[MAX_PIN_LENGTH+1];
	char *prompt;
	UI *ui;
	int rv;

	/* Handle CKF_PROTECTED_AUTHENTICATION_PATH */
	if (slot->secure_login) {
		rv = CRYPTOKI_call(ctx,
			C_Login(session, CKU_CONTEXT_SPECIFIC, NULL, 0));
		return rv == CKR_USER_ALREADY_LOGGED_IN ? 0 : rv;
	}

	/* Call UI to ask for a PIN */
	ui = UI_new_method(ctx->ui_method);
	if (!ui)
		return P11_R_UI_FAILED;
	if (ctx->ui_user_data)
		UI_add_user_data(ui, ctx->ui_user_data);
	memset(pin, 0, MAX_PIN_LENGTH+1);
	prompt = UI_construct_prompt(ui, "PKCS#11 key PIN", key->label);
	if (!prompt) {
		return P11_R_UI_FAILED;
	}
	if (UI_dup_input_string(ui, prompt,
			UI_INPUT_FLAG_DEFAULT_PWD, pin, 4, MAX_PIN_LENGTH) <= 0) {
		UI_free(ui);
		OPENSSL_free(prompt);
		return P11_R_UI_FAILED;
	}
	OPENSSL_free(prompt);

	if (UI_process(ui)) {
		UI_free(ui);
		return P11_R_UI_FAILED;
	}
	UI_free(ui);

	/* Login with the PIN */
	rv = CRYPTOKI_call(ctx,
		C_Login(session, CKU_CONTEXT_SPECIFIC,
			(CK_UTF8CHAR *)pin, (CK_ULONG)strlen(pin)));
	OPENSSL_cleanse(pin, MAX_PIN_LENGTH+1);
	return rv == CKR_USER_ALREADY_LOGGED_IN ? 0 : rv;
}

/*
 * Return keys of a given type (public or private) matching the key_template
 * Use the cached values if available
 */
int pkcs11_enumerate_keys(PKCS11_SLOT_private *slot, unsigned int type, const PKCS11_KEY *key_template,
		PKCS11_KEY **keyp, unsigned int *countp)
{
	PKCS11_keys *keys = (type == CKO_PRIVATE_KEY) ? &slot->prv : &slot->pub;
	PKCS11_TEMPLATE tmpl = {0};
	CK_SESSION_HANDLE session;
	CK_OBJECT_CLASS object_class = type;
	int rv;

	pkcs11_addattr_var(&tmpl, CKA_CLASS, object_class);
	if (key_template) {
		if (key_template->id_len)
			pkcs11_addattr(&tmpl, CKA_ID, key_template->id, key_template->id_len);

		if (key_template->label)
			pkcs11_addattr_s(&tmpl, CKA_LABEL, key_template->label);
	}
	if (pkcs11_get_session(slot, 0, &session)) {
		pkcs11_zap_attrs(&tmpl);
		return -1;
	}

	rv = pkcs11_find_keys(slot, session, type, &tmpl);

	pkcs11_zap_attrs(&tmpl);
	pkcs11_put_session(slot, session);
	if (rv < 0) {
		pkcs11_destroy_keys(slot, type);
		return -1;
	}
	if (keyp)
		*keyp = keys->keys;
	if (countp)
		*countp = keys->num;
	return 0;
}

/**
 * Remove an object from the associated token
 */
int pkcs11_remove_object(PKCS11_OBJECT_private *obj)
{
	PKCS11_SLOT_private *slot = obj->slot;
	PKCS11_CTX_private *ctx = slot->ctx;
	CK_SESSION_HANDLE session;
	int rv;

	if (pkcs11_get_session(slot, 1, &session))
		return -1;

	rv = CRYPTOKI_call(ctx, C_DestroyObject(session, obj->object));
	pkcs11_put_session(slot, session);
	CRYPTOKI_checkerr(CKR_F_PKCS11_REMOVE_KEY, rv);

	return 0;
}

/*
 * Find all keys of a given type (public or private) matching template
 */
static int pkcs11_find_keys(PKCS11_SLOT_private *slot, CK_SESSION_HANDLE session, unsigned int type, PKCS11_TEMPLATE *tmpl)
{
	PKCS11_CTX_private *ctx = slot->ctx;
	int rv, res = -1;

	/* Tell the PKCS11 lib to enumerate all matching objects */
	rv = CRYPTOKI_call(ctx,
		C_FindObjectsInit(session, tmpl->attrs, tmpl->nattr));
	CRYPTOKI_checkerr(CKR_F_PKCS11_FIND_KEYS, rv);

	do {
		res = pkcs11_next_key(ctx, slot, session, type);
	} while (res == 0);

	CRYPTOKI_call(ctx, C_FindObjectsFinal(session));

	return (res < 0) ? -1 : 0;
}

static int pkcs11_next_key(PKCS11_CTX_private *ctx, PKCS11_SLOT_private *slot,
		CK_SESSION_HANDLE session, CK_OBJECT_CLASS type)
{
	CK_OBJECT_HANDLE obj;
	CK_ULONG count;
	int rv;

	/* Get the next matching object */
	rv = CRYPTOKI_call(ctx, C_FindObjects(session, &obj, 1, &count));
	CRYPTOKI_checkerr(CKR_F_PKCS11_NEXT_KEY, rv);

	if (count == 0)
		return 1;

	if (pkcs11_init_key(slot, session, obj, type, NULL))
		return -1;

	return 0;
}

PKCS11_OBJECT_private *pkcs11_object_ref(PKCS11_OBJECT_private *obj)
{
	pkcs11_atomic_add(&obj->refcnt, 1, &obj->lock);
	return obj;
}

static int pkcs11_init_key(PKCS11_SLOT_private *slot, CK_SESSION_HANDLE session,
	CK_OBJECT_HANDLE object, CK_OBJECT_CLASS type, PKCS11_KEY **ret)
{
	PKCS11_keys *keys = (type == CKO_PRIVATE_KEY) ? &slot->prv : &slot->pub;
	PKCS11_OBJECT_private *kpriv;
	PKCS11_KEY *key, *tmp;
	int i;

	/* Prevent re-adding existing PKCS#11 object handles */
	/* TODO: Rewrite the O(n) algorithm as O(log n),
	 * or it may be too slow with a large number of keys */
	for (i = 0; i < keys->num; ++i) {
		if (keys->keys[i]._private->object == object) {
			if (ret)
				*ret = &keys->keys[i];
			return 0;
		}
	}

	kpriv = pkcs11_object_from_handle(slot, session, object);
	if (!kpriv)
		return -1;

	/* Allocate memory */
	tmp = OPENSSL_realloc(keys->keys, (keys->num + 1) * sizeof(PKCS11_KEY));
	if (!tmp) {
		pkcs11_object_free(kpriv);
		return -1;
	}
	keys->keys = tmp;
	key = keys->keys + keys->num++;
	memset(key, 0, sizeof(PKCS11_KEY));

	/* Fill public properties */
	key->_private = kpriv;
	key->id = kpriv->id;
	key->id_len = kpriv->id_len;
	key->label = kpriv->label;
	key->isPrivate = (type == CKO_PRIVATE_KEY);

	/* Link back */
	kpriv->public = key;

	if (ret)
		*ret = key;
	return 0;
}

/* Convert an OpenSSL digest (EVP_MD) to the corresponding PKCS#11 hash mechanism */
CK_MECHANISM_TYPE pkcs11_md2ckm(const EVP_MD *md)
{
	switch (EVP_MD_type(md)) {
	case NID_sha1:
		return CKM_SHA_1;
	case NID_sha224:
		return CKM_SHA224;
	case NID_sha256:
		return CKM_SHA256;
	case NID_sha512:
		return CKM_SHA512;
	case NID_sha384:
		return CKM_SHA384;
#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER)
	case NID_sha3_224:
		return CKM_SHA3_224;
	case NID_sha3_256:
		return CKM_SHA3_256;
	case NID_sha3_384:
		return CKM_SHA3_384;
	case NID_sha3_512:
		return CKM_SHA3_512;
#endif
	default:
		return 0;
	}
}

/* Convert an OpenSSL digest (EVP_MD) to the corresponding PKCS#11 MGF1 identifier */
CK_RSA_PKCS_MGF_TYPE pkcs11_md2ckg(const EVP_MD *md)
{
	switch (EVP_MD_type(md)) {
	case NID_sha1:
		return CKG_MGF1_SHA1;
	case NID_sha224:
		return CKG_MGF1_SHA224;
	case NID_sha256:
		return CKG_MGF1_SHA256;
	case NID_sha512:
		return CKG_MGF1_SHA512;
	case NID_sha384:
		return CKG_MGF1_SHA384;
#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER)
	case NID_sha3_224:
		return CKG_MGF1_SHA3_224;
	case NID_sha3_256:
		return CKG_MGF1_SHA3_256;
	case NID_sha3_384:
		return CKG_MGF1_SHA3_384;
	case NID_sha3_512:
		return CKG_MGF1_SHA3_512;
#endif
	default:
		return 0;
	}
}

static int pkcs11_init_keygen(PKCS11_SLOT_private *slot, CK_SESSION_HANDLE *session)
{
	pthread_mutex_lock(&slot->lock);
	/* R/W session is mandatory for key generation. */
	if (slot->rw_mode != 1) {
		pthread_mutex_unlock(&slot->lock);
		if (pkcs11_open_session(slot, 1))
			return -1;
		/* open_session will call C_CloseAllSessions which logs everyone out */
		if (pkcs11_login(slot, 0, slot->prev_pin))
			return -1;
	}
	pthread_mutex_unlock(&slot->lock);
	return pkcs11_get_session(slot, 1, session);
}

static void pkcs11_common_pubkey_attr(PKCS11_TEMPLATE *pubtmpl,
		const char *label, const unsigned char *id, size_t id_len)
{
	/* Common pubkey attributes */
	pkcs11_addattr(pubtmpl, CKA_ID, (void *)id, id_len);
	if (label)
		pkcs11_addattr_s(pubtmpl, CKA_LABEL, label);
	pkcs11_addattr_bool(pubtmpl, CKA_TOKEN, TRUE);
	pkcs11_addattr_bool(pubtmpl, CKA_VERIFY, TRUE);
	pkcs11_addattr_bool(pubtmpl, CKA_WRAP, TRUE);
}

static void pkcs11_common_privkey_attr(PKCS11_TEMPLATE *privtmpl,
		const char *label, const unsigned char *id, size_t id_len,
		const PKCS11_params *params)
{
	/* Common privkey attributes */
	pkcs11_addattr(privtmpl, CKA_ID, (void *)id, id_len);
	if (label)
		pkcs11_addattr_s(privtmpl, CKA_LABEL, label);
	pkcs11_addattr_bool(privtmpl, CKA_PRIVATE, TRUE);
	pkcs11_addattr_bool(privtmpl, CKA_TOKEN, TRUE);
	pkcs11_addattr_bool(privtmpl, CKA_SENSITIVE, params->sensitive);
	pkcs11_addattr_bool(privtmpl, CKA_EXTRACTABLE, params->extractable);
	pkcs11_addattr_bool(privtmpl, CKA_SIGN, TRUE);
	pkcs11_addattr_bool(privtmpl, CKA_UNWRAP, TRUE);
}

#if OPENSSL_VERSION_NUMBER >= 0x30500000L
#ifndef OPENSSL_NO_ML_DSA

static void pkcs11_luna_pubkey_attr(PKCS11_TEMPLATE *pubtmpl,
		const char *label, const unsigned char *id, size_t id_len)
{
	/* Thales Luna HSM pubkey attributes */
	pkcs11_addattr(pubtmpl, CKA_ID, (void *)id, id_len);
	if (label)
		pkcs11_addattr_s(pubtmpl, CKA_LABEL, label);
	pkcs11_addattr_bool(pubtmpl, CKA_TOKEN, TRUE);
	pkcs11_addattr_bool(pubtmpl, CKA_VERIFY, TRUE);
}

static void pkcs11_luna_privkey_attr(PKCS11_TEMPLATE *privtmpl,
		const char *label, const unsigned char *id, size_t id_len,
		const PKCS11_params *params)
{
	/* Thales Luna HSM privkey attributes */
	pkcs11_addattr(privtmpl, CKA_ID, (void *)id, id_len);
	if (label)
		pkcs11_addattr_s(privtmpl, CKA_LABEL, label);
	pkcs11_addattr_bool(privtmpl, CKA_PRIVATE, TRUE);
	pkcs11_addattr_bool(privtmpl, CKA_TOKEN, TRUE);
	pkcs11_addattr_bool(privtmpl, CKA_SENSITIVE, params->sensitive);
	pkcs11_addattr_bool(privtmpl, CKA_EXTRACTABLE, params->extractable);
	pkcs11_addattr_bool(privtmpl, CKA_SIGN, TRUE);
}

static PKCS11_OBJECT_ops *pkcs11_mldsa_ops_from_param(CK_ULONG param_set)
{
	switch (param_set) {
	case CKP_ML_DSA_44:
		return &pkcs11_mldsa44_ops;
	case CKP_ML_DSA_65:
		return &pkcs11_mldsa65_ops;
	case CKP_ML_DSA_87:
		return &pkcs11_mldsa87_ops;
	default:
		return NULL;
	}
}
#endif /* OPENSSL_NO_ML_DSA */

#ifndef OPENSSL_NO_SLH_DSA
static PKCS11_OBJECT_ops *pkcs11_slhdsa_ops_from_param(CK_ULONG param_set)
{
	switch (param_set) {
	case CKP_SLH_DSA_SHA2_128S:
		return &pkcs11_slhdsa_sha2_128s_ops;
	case CKP_SLH_DSA_SHAKE_128S:
		return &pkcs11_slhdsa_shake_128s_ops;
	case CKP_SLH_DSA_SHA2_128F:
		return &pkcs11_slhdsa_sha2_128f_ops;
	case CKP_SLH_DSA_SHAKE_128F:
		return &pkcs11_slhdsa_shake_128f_ops;
	case CKP_SLH_DSA_SHA2_192S:
		return &pkcs11_slhdsa_sha2_192s_ops;
	case CKP_SLH_DSA_SHAKE_192S:
		return &pkcs11_slhdsa_shake_192s_ops;
	case CKP_SLH_DSA_SHA2_192F:
		return &pkcs11_slhdsa_sha2_192f_ops;
	case CKP_SLH_DSA_SHAKE_192F:
		return &pkcs11_slhdsa_shake_192f_ops;
	case CKP_SLH_DSA_SHA2_256S:
		return &pkcs11_slhdsa_sha2_256s_ops;
	case CKP_SLH_DSA_SHAKE_256S:
		return &pkcs11_slhdsa_shake_256s_ops;
	case CKP_SLH_DSA_SHA2_256F:
		return &pkcs11_slhdsa_sha2_256f_ops;
	case CKP_SLH_DSA_SHAKE_256F:
		return &pkcs11_slhdsa_shake_256f_ops;
	default:
		return NULL;
	}
}
#endif /* OPENSSL_NO_SLH_DSA */
#endif /* OPENSSL_VERSION_NUMBER >= 0x30500000L */

#if OPENSSL_VERSION_NUMBER >= 0x30000000L
static PKCS11_OBJECT_ops *pkcs11_falcon_ops_from_param(CK_ULONG param_set)
{
	switch (param_set) {
	case CKP_FALCON_512:
		return &pkcs11_falcon512_ops;
	case CKP_FALCON_1024:
		return &pkcs11_falcon1024_ops;
	default:
		return NULL;
	}
}
#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */

#if OPENSSL_VERSION_NUMBER < 0x30000000L
static int EVP_PKEY_is_a(const EVP_PKEY *pkey, const char *name)

{
	if (pkey == NULL || name == NULL)
		return 0;

	switch (EVP_PKEY_base_id(pkey)) {
	case EVP_PKEY_RSA:
		return strcmp(name, "RSA") == 0;
#ifdef EVP_PKEY_RSA_PSS
	case EVP_PKEY_RSA_PSS:
		return strcmp(name, "RSA-PSS") == 0;
#endif /* EVP_PKEY_RSA_PSS */
	case EVP_PKEY_EC:
		return strcmp(name, "EC") == 0;
#ifndef OPENSSL_NO_ECX
#ifdef EVP_PKEY_ED25519
	case EVP_PKEY_ED25519:
		return strcmp(name, "ED25519") == 0;
#endif /* EVP_PKEY_ED25519 */
#ifdef EVP_PKEY_ED448
	case EVP_PKEY_ED448:
		return strcmp(name, "ED448") == 0;
#endif /* EVP_PKEY_ED448 */
#endif /* OPENSSL_NO_ECX */
	default:
		return 0;
    }
}
#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */

/*
 * Destroy all keys of a given type (public or private)
 */
void pkcs11_destroy_keys(PKCS11_SLOT_private *slot, unsigned int type)
{
	PKCS11_keys *keys = (type == CKO_PRIVATE_KEY) ? &slot->prv : &slot->pub;

	while (keys->num > 0) {
		PKCS11_KEY *key = &keys->keys[--keys->num];
		PKCS11_OBJECT_private *obj = key->_private;

		if (obj) {
			EVP_PKEY_free(obj->evp_key);
			pkcs11_object_free(obj);
		}
	}
	if (keys->keys)
		OPENSSL_free(keys->keys);
	keys->keys = NULL;
	keys->num = 0;
}

#if OPENSSL_VERSION_NUMBER >= 0x30000000L
static void pkcs11_set_ex_data_evp_pkey(EVP_PKEY *pkey, PKCS11_KEY *key)
{
	PKCS11_OBJECT_private *obj;

	if (pkey == NULL || key == NULL || key->_private == NULL)
		return;

	obj = pkcs11_object_ref(key->_private);
	if (obj == NULL)
		return;

	if (!EVP_PKEY_set_ex_data(pkey, evp_pkey_ex_index, obj))
		pkcs11_object_free(obj);
}

static int pkcs11_dup_ex_data_evp_pkey(CRYPTO_EX_DATA *to,
		const CRYPTO_EX_DATA *from, void **from_d,
		int idx, long argl, void *argp)
{
	PKCS11_OBJECT_private *obj = *from_d;

	(void)to;
	(void)from;
	(void)idx;
	(void)argl;
	(void)argp;

	if (obj != NULL)
		*from_d = pkcs11_object_ref(obj);

	return 1;
}

static void pkcs11_free_ex_data_evp_pkey(void *parent, void *ptr,
		CRYPTO_EX_DATA *ad, int idx, long argl, void *argp)
{
	(void)parent;
	(void)ad;
	(void)idx;
	(void)argl;
	(void)argp;

	pkcs11_object_free(ptr);
}

static PKCS11_OBJECT_private *pkcs11_get_ex_data_evp_pkey(const EVP_PKEY *pkey)
{
	return EVP_PKEY_get_ex_data(pkey, evp_pkey_ex_index);
}

static void alloc_evp_pkey_ex_index(void)
{
	if (evp_pkey_ex_index == 0) {
		while (evp_pkey_ex_index == 0) /* Workaround for OpenSSL RT3710 */
			evp_pkey_ex_index = EVP_PKEY_get_ex_new_index(0,
				"libp11 EVP_PKEY", NULL,
				pkcs11_dup_ex_data_evp_pkey,
				pkcs11_free_ex_data_evp_pkey);
		if (evp_pkey_ex_index < 0)
			evp_pkey_ex_index = 0; /* Fallback to app_data */
	}
}

void free_evp_pkey_ex_index(void)
{
	if (evp_pkey_ex_index > 0) {
		CRYPTO_free_ex_index(CRYPTO_EX_INDEX_EVP_PKEY, evp_pkey_ex_index);
		evp_pkey_ex_index = 0;
	}
}
#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */


#if OPENSSL_VERSION_NUMBER < 0x40000000L
# if OPENSSL_VERSION_NUMBER >= 0x30000000L
void pkcs11_set_ex_data_pkey(EVP_PKEY *pkey, PKCS11_OBJECT_private *key)
{
	EVP_PKEY_set_ex_data(pkey, pkey_ex_index, key);
}

PKCS11_OBJECT_private *pkcs11_get_ex_data_pkey(const EVP_PKEY *pkey)
{
	return EVP_PKEY_get_ex_data(pkey, pkey_ex_index);
}

void alloc_pkey_ex_index(void)
{
	if (pkey_ex_index == 0) {
		while (pkey_ex_index == 0) /* Workaround for OpenSSL RT3710 */
			pkey_ex_index = EVP_PKEY_get_ex_new_index(0, "libp11 PKCS11_KEY",
				NULL, NULL, NULL);
		if (pkey_ex_index < 0)
			pkey_ex_index = 0; /* Fallback to app_data */
	}
}

void free_pkey_ex_index(void)
{
	if (pkey_ex_index > 0) {
		CRYPTO_free_ex_index(CRYPTO_EX_INDEX_EVP_PKEY, pkey_ex_index);
		pkey_ex_index = 0;
	}
}
# endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */

/* Attempt to sign using the PKCS#11-backed RSA implementation */
static int pkcs11_try_pkey_rsa_sign(EVP_PKEY_CTX *evp_pkey_ctx,
		unsigned char *sig, size_t *siglen,
		const unsigned char *tbs, size_t tbslen)
{
	EVP_PKEY *pkey;
	RSA *rsa;
	int padding;
	PKCS11_OBJECT_private *key;
	PKCS11_SLOT_private *slot;
	const EVP_MD *md, *mgf1_md;
	CK_SESSION_HANDLE session;
	const char *mdname, *mgf1_mdname;
	int salt_len;

	/* RSA method has EVP_PKEY_FLAG_AUTOARGLEN set. OpenSSL core will handle
	 * the size inquiry internally. */
	if (!sig)
		return -1;

	if (!evp_pkey_ctx)
		return -1;

	pkey = EVP_PKEY_CTX_get0_pkey(evp_pkey_ctx);
	if (!pkey)
		return -1;

	rsa = (RSA *)EVP_PKEY_get0_RSA(pkey);
	if (!rsa)
		return -1;

	key = pkcs11_get_ex_data_rsa(rsa);
	if (check_object_fork(key) < 0)
		return -1;

	slot = key->slot;
	if (!slot)
		return -1;

	if (pkcs11_get_session(slot, 0, &session))
		return -1;

	pkcs11_put_session(slot, session);

	/* retrieve PSS parameters */
	if (EVP_PKEY_CTX_get_rsa_padding(evp_pkey_ctx, &padding) <= 0)
		return -1;

	if (padding != RSA_PKCS1_PSS_PADDING)
		return -1; /* unsupported */

	if (EVP_PKEY_CTX_get_signature_md(evp_pkey_ctx, &md) <= 0)
		return -1;

	if (tbslen != (size_t)EVP_MD_size(md))
		return -1;

	if (EVP_PKEY_CTX_get_rsa_mgf1_md(evp_pkey_ctx, &mgf1_md) <= 0)
		return -1;

	if (EVP_PKEY_CTX_get_rsa_pss_saltlen(evp_pkey_ctx, &salt_len) == 0)
		return -1;

	mdname = EVP_MD_name(md);
	mgf1_mdname = EVP_MD_name(mgf1_md);

	return pkcs11_evp_pkey_rsa_sign(key, pkey, mdname, padding,
		salt_len, mgf1_mdname, sig, siglen, tbs, tbslen);
}

/* Attempt to decrypt using the PKCS#11-backed RSA implementation */
static int pkcs11_try_pkey_rsa_decrypt(EVP_PKEY_CTX *evp_pkey_ctx,
		unsigned char *out, size_t *outlen,
		const unsigned char *in, size_t inlen)
{
	EVP_PKEY *pkey;
	RSA *rsa;
	int padding;
	PKCS11_OBJECT_private *key;
	PKCS11_SLOT_private *slot;
	CK_SESSION_HANDLE session;
	const EVP_MD *md, *mgf1_md;
	const char *mdname = NULL, *mgf1_mdname = NULL;
	unsigned char *oaep_label = NULL;
	int oaep_labellen = 0;

	/* RSA method has EVP_PKEY_FLAG_AUTOARGLEN set. OpenSSL core will handle
	 * the size inquiry internally. */
	if (!out)
		return -1;

	if (!evp_pkey_ctx)
		return -1;

	pkey = EVP_PKEY_CTX_get0_pkey(evp_pkey_ctx);
	if (!pkey)
		return -1;

	rsa = (RSA *)EVP_PKEY_get0_RSA(pkey);
	if (!rsa)
		return -1;

	key = pkcs11_get_ex_data_rsa(rsa);
	if (check_object_fork(key) < 0)
		return -1;

	/* check RSA padding */
	if (EVP_PKEY_CTX_get_rsa_padding(evp_pkey_ctx, &padding) <= 0)
		return -1;

	slot = key->slot;
	if (!slot)
		return -1;

	if (pkcs11_get_session(slot, 0, &session))
		return -1;

	pkcs11_put_session(slot, session);

	switch (padding) {
	case RSA_PKCS1_PADDING:
		break;

	case RSA_PKCS1_OAEP_PADDING:
		/* retrieve OAEP parameters */
		if (EVP_PKEY_CTX_get_rsa_oaep_md(evp_pkey_ctx, &md) <= 0 ||
				md == NULL)
			return -1;

		if (EVP_PKEY_CTX_get_rsa_mgf1_md(evp_pkey_ctx, &mgf1_md) <= 0 ||
				mgf1_md == NULL)
			return -1;

		mdname = EVP_MD_name(md);
		mgf1_mdname = EVP_MD_name(mgf1_md);

		oaep_labellen = EVP_PKEY_CTX_get0_rsa_oaep_label(evp_pkey_ctx,
			&oaep_label);
		if (oaep_labellen < 0) {
			oaep_labellen = 0;
			oaep_label = NULL;
		}
		break;

	default:
		return -1;
	}

	return pkcs11_evp_pkey_rsa_decrypt(key, mdname, padding, mgf1_mdname,
		oaep_label, oaep_labellen, out, outlen, in, inlen);
}

static int pkcs11_pkey_rsa_sign(EVP_PKEY_CTX *evp_pkey_ctx,
		unsigned char *sig, size_t *siglen,
		const unsigned char *tbs, size_t tbslen)
{
	int ret;

	ret = pkcs11_try_pkey_rsa_sign(evp_pkey_ctx, sig, siglen, tbs, tbslen);
	if (ret < 0)
		ret = (*orig_pkey_rsa_sign)(evp_pkey_ctx, sig, siglen, tbs, tbslen);
	return ret;
}

static int pkcs11_pkey_rsa_decrypt(EVP_PKEY_CTX *evp_pkey_ctx,
		unsigned char *out, size_t *outlen,
		const unsigned char *in, size_t inlen)
{
	int ret;

	ret = pkcs11_try_pkey_rsa_decrypt(evp_pkey_ctx, out, outlen, in, inlen);
	if (ret < 0)
		ret = (*orig_pkey_rsa_decrypt)(evp_pkey_ctx, out, outlen, in, inlen);
	return ret;
}

#if OPENSSL_VERSION_NUMBER < 0x100020d0L || defined(LIBRESSL_VERSION_NUMBER)
void EVP_PKEY_meth_get_sign(EVP_PKEY_METHOD *pmeth,
		int (**psign_init) (EVP_PKEY_CTX *ctx),
		int (**psign) (EVP_PKEY_CTX *ctx,
			unsigned char *sig, size_t *siglen,
			const unsigned char *tbs, size_t tbslen))
{
	if (psign_init)
		*psign_init = pmeth->sign_init;
	if (psign)
		*psign = pmeth->sign;
}

static void EVP_PKEY_meth_get_decrypt(EVP_PKEY_METHOD *pmeth,
		int (**pdecrypt_init) (EVP_PKEY_CTX *ctx),
		int (**pdecrypt) (EVP_PKEY_CTX *ctx,
			unsigned char *out,
			size_t *outlen,
			const unsigned char *in,
			size_t inlen))
{
	if (pdecrypt_init)
		*pdecrypt_init = pmeth->decrypt_init;
	if (pdecrypt)
		*pdecrypt = pmeth->decrypt;
}
#endif

EVP_PKEY_METHOD *pkcs11_pkey_method_rsa(void)
{
	EVP_PKEY_METHOD *new_meth_rsa = NULL;
	int orig_id;

	/* Cache the original EVP_PKEY_RSA method (once) */
	if (!orig_method_rsa)
#if OPENSSL_VERSION_NUMBER < 0x10101000L || defined(LIBRESSL_VERSION_NUMBER)
		orig_method_rsa = (EVP_PKEY_METHOD *)EVP_PKEY_meth_find(EVP_PKEY_RSA);
#else
		orig_method_rsa = EVP_PKEY_meth_find(EVP_PKEY_RSA);
#endif /* OPENSSL_VERSION_NUMBER < 0x10101000L || defined(LIBRESSL_VERSION_NUMBER) */

	if (!orig_method_rsa)
		return NULL;

	EVP_PKEY_meth_get0_info(&orig_id, NULL, orig_method_rsa);
	if (orig_id != EVP_PKEY_RSA)
		return NULL;

	EVP_PKEY_meth_get_sign(orig_method_rsa,
		&orig_pkey_rsa_sign_init, &orig_pkey_rsa_sign);
	if (!orig_pkey_rsa_sign)
		return NULL;

	EVP_PKEY_meth_get_decrypt(orig_method_rsa,
		&orig_pkey_rsa_decrypt_init, &orig_pkey_rsa_decrypt);
	if (!orig_pkey_rsa_decrypt)
		return NULL;

	new_meth_rsa = EVP_PKEY_meth_new(EVP_PKEY_RSA, EVP_PKEY_FLAG_AUTOARGLEN);
	if (!new_meth_rsa)
		return NULL;

	/* Duplicate the original method */
	EVP_PKEY_meth_copy(new_meth_rsa, orig_method_rsa);

	EVP_PKEY_meth_set_sign(new_meth_rsa,
		orig_pkey_rsa_sign_init, pkcs11_pkey_rsa_sign);
	EVP_PKEY_meth_set_decrypt(new_meth_rsa,
		orig_pkey_rsa_decrypt_init, pkcs11_pkey_rsa_decrypt);

	return new_meth_rsa;
}
#endif /* OPENSSL_VERSION_NUMBER < 0x40000000L */

/* vim: set noexpandtab: */
