/* 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>
 *
 *  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
 */

/*
 * This file implements the handling of RSA keys stored on a
 * PKCS11 token
 */

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

static int rsa_ex_index = 0;
static RSA_METHOD *pkcs11_rsa_method = NULL;

#if OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_VERSION_NUMBER < 0x40000000L
static EVP_PKEY_METHOD *pkey_method_rsa  = NULL;
#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_VERSION_NUMBER < 0x40000000L */

static RSA *pkcs11_get1_rsa(PKCS11_OBJECT_private *key)
{
	EVP_PKEY *evp_key = pkcs11_get_key(key, key->object_class);
	RSA *rsa;

	if (!evp_key)
		return NULL;
	rsa = (RSA *)EVP_PKEY_get1_RSA(evp_key);
	EVP_PKEY_free(evp_key);

	return rsa;
}

/* PKCS#1 v1.5 RSA signature */
/* TODO: remove this function in libp11 0.5.0 */
int pkcs11_sign(int type, const unsigned char *m, unsigned int m_len,
		unsigned char *sigret, unsigned int *siglen, PKCS11_OBJECT_private *key)
{
	RSA *rsa = pkcs11_get1_rsa(key);
	int ret;

	if (!rsa)
		return -1;

	ret = RSA_sign(type, m, m_len, sigret, siglen, rsa);

	RSA_free(rsa);
	return ret;
}

/*
 * RSA private key encryption (also invoked by OpenSSL for signing)
 * OpenSSL assumes that the output buffer is always big enough
 */
int pkcs11_private_encrypt(int flen,
		const unsigned char *from, unsigned char *to,
		PKCS11_OBJECT_private *key, int padding)
{
	PKCS11_SLOT_private *slot;
	size_t siglen;
	CK_SESSION_HANDLE session;

	if (!key)
		return -1;

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

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

	pkcs11_put_session(slot, session);

	siglen = pkcs11_get_key_size(key);
	if (pkcs11_evp_pkey_rsa_sign(key,
		NULL, /* EVP_PKEY unused: RSA-PSS unsupported in ENGINE path */
		NULL, /* digest metadata unavailable, input is already encoded/padded if needed */
		padding,
		0, NULL, /* unsupported PSS parameters */
		to, &siglen, from, flen) <= 0)
		return -1;

	return (int)siglen;
}

/* RSA private key decryption */
int pkcs11_private_decrypt(int flen,
		const unsigned char *from, unsigned char *to,
		PKCS11_OBJECT_private *key, int padding)
{
	PKCS11_SLOT_private *slot;
	size_t outlen;
	CK_SESSION_HANDLE session;

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

	if (!key)
		return -1;

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

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

	pkcs11_put_session(slot, session);

	/* Openssl API for RSA_private_decrypt() allows to use
	 * RSA_PKCS1_OAEP_PADDING only with SHA_1 hash and and MGF1_SHA1 mask
	 * gen function.  It is not possible to use RFC8017 "Label" or
	 * PKCS#11 "source data" respectively.
	 * https://www.openssl.org/docs/man3.0/man3/RSA_private_decrypt.html */
	outlen = flen;
	if (pkcs11_evp_pkey_rsa_decrypt(key, "SHA1", padding, "SHA1",
		NULL, 0, /* unsupported oaep_label */
		to, &outlen, from, flen) <= 0)
		return -1;

	return (int)outlen;
}

/* TODO: remove this function in libp11 0.5.0 */
int pkcs11_verify(int type, const unsigned char *m, unsigned int m_len,
		unsigned char *signature, unsigned int siglen, PKCS11_OBJECT_private *key)
{
	(void)type;
	(void)m;
	(void)m_len;
	(void)signature;
	(void)siglen;
	(void)key;

	/* PKCS11 calls go here */
	P11err(P11_F_PKCS11_VERIFY, P11_R_NOT_SUPPORTED);
	return -1;
}

/*
 * Get RSA key material
 */
static RSA *pkcs11_get_rsa(PKCS11_OBJECT_private *key)
{
	CK_OBJECT_CLASS class_public_key = CKO_PUBLIC_KEY;
	PKCS11_SLOT_private *slot = key->slot;
	PKCS11_CTX_private *ctx = slot->ctx;
	PKCS11_OBJECT_private *pubkey;
	PKCS11_TEMPLATE tmpl = {0};
	CK_OBJECT_HANDLE object = key->object;
	CK_SESSION_HANDLE session;
	RSA *rsa;
	BIGNUM *rsa_n = NULL, *rsa_e = NULL;

	if (pkcs11_get_session(slot, 0, &session))
		return NULL;

	/* Retrieve the modulus */
	if (pkcs11_getattr_bn(ctx, session, object, CKA_MODULUS, &rsa_n))
		goto failure;

	/* Retrieve the public exponent */
	if (!pkcs11_getattr_bn(ctx, session, object, CKA_PUBLIC_EXPONENT, &rsa_e)) {
		if (!BN_is_zero(rsa_e)) /* A valid public exponent */
			goto success;
		BN_clear_free(rsa_e);
		rsa_e = NULL;
	}

	/* The public exponent was not found in the private key:
	 * retrieve it from the corresponding public key */
	pkcs11_addattr_var(&tmpl, CKA_CLASS, class_public_key);
	pkcs11_addattr_bn(&tmpl, CKA_MODULUS, rsa_n);
	pubkey = pkcs11_object_from_template(slot, session, &tmpl);
	if (pubkey && !pkcs11_getattr_bn(ctx, session, pubkey->object,
			CKA_PUBLIC_EXPONENT, &rsa_e)) {
		pkcs11_object_free(pubkey);
		goto success;
	}
	pkcs11_object_free(pubkey);

	/* Last resort: use the most common default */
	rsa_e = BN_new();
	if (rsa_e && BN_set_word(rsa_e, RSA_F4))
		goto success;

failure:
	pkcs11_put_session(slot, session);
	/* BN_clear_free() is NULL-safe */
	BN_clear_free(rsa_n);
	BN_clear_free(rsa_e);
	return NULL;

success:
	pkcs11_put_session(slot, session);
	rsa = RSA_new();
	if (!rsa) {
		BN_clear_free(rsa_n);
		BN_clear_free(rsa_e);
		return NULL;
	}
#if OPENSSL_VERSION_NUMBER >= 0x10100005L || ( defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER >= 0x3050000fL )
	RSA_set0_key(rsa, rsa_n, rsa_e, NULL);
#else
	rsa->n = rsa_n;
	rsa->e = rsa_e;
#endif
	return rsa;
}

PKCS11_OBJECT_private *pkcs11_get_ex_data_rsa(const RSA *rsa)
{
	return RSA_get_ex_data(rsa, rsa_ex_index);
}

void pkcs11_set_ex_data_rsa(RSA *rsa, PKCS11_OBJECT_private *key)
{
	RSA_set_ex_data(rsa, rsa_ex_index, key);
}

#if OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_VERSION_NUMBER < 0x40000000L

/* Global initialize RSA EVP_PKEY_METHOD */
static int pkcs11_pkey_method_rsa_new(void)
{
	if (pkey_method_rsa)
		return 1; /* EVP_PKEY_RSA method already initialized */

	pkey_method_rsa = pkcs11_pkey_method_rsa();
	if (!pkey_method_rsa)
		return 0;

	/* Register the method globally */
	if (!EVP_PKEY_meth_add0(pkey_method_rsa)) {
		EVP_PKEY_meth_free(pkey_method_rsa);
		pkey_method_rsa = NULL;
		return 0;
	}
	return 1;
}

void pkcs11_rsa_key_method_free(void)
{
	if (pkey_method_rsa) {
		free_pkey_ex_index();
		EVP_PKEY_meth_remove(pkey_method_rsa);
		EVP_PKEY_meth_free(pkey_method_rsa);
		pkey_method_rsa = NULL;
	}
}

#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_VERSION_NUMBER < 0x40000000L */

/*
 * Build an EVP_PKEY object
 */
static EVP_PKEY *pkcs11_get_evp_key_rsa(PKCS11_OBJECT_private *key)
{
	EVP_PKEY *pk;
	RSA *rsa;

	rsa = pkcs11_get_rsa(key);
	if (!rsa)
		return NULL;
	pk = EVP_PKEY_new();
	if (!pk) {
		RSA_free(rsa);
		return NULL;
	}
	if (key->object_class == CKO_PRIVATE_KEY) {
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
# if OPENSSL_VERSION_NUMBER < 0x40000000L
		if ((key->slot->ctx->flags & PKCS11_FLAG_NO_METHODS) == 0) {
			/* global initialize RSA EVP_PKEY_METHOD */
			if (!pkcs11_pkey_method_rsa_new()) {
				EVP_PKEY_free(pk);
				return NULL;
			}
			alloc_pkey_ex_index();
			pkcs11_set_ex_data_pkey(pk, key);
			atexit(pkcs11_rsa_key_method_free);
		}
# endif /* OPENSSL_VERSION_NUMBER < 0x40000000L */
		/* creates a new EVP_PKEY object which requires its own key object reference */
		key = pkcs11_object_ref(key);
#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */

		RSA_set_method(rsa, PKCS11_get_rsa_method());
#if OPENSSL_VERSION_NUMBER >= 0x10100005L || ( defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER >= 0x3050000fL )
		RSA_set_flags(rsa, RSA_FLAG_EXT_PKEY);
#else
		rsa->flags |= RSA_FLAG_EXT_PKEY;
#endif
	}
	/* TODO: Retrieve the RSA private key object attributes instead,
	 * unless the key has the "sensitive" attribute set */

#if OPENSSL_VERSION_NUMBER < 0x01010000L
	/* RSA_FLAG_SIGN_VER is no longer needed since OpenSSL 1.1 */
	rsa->flags |= RSA_FLAG_SIGN_VER;
#endif
	pkcs11_set_ex_data_rsa(rsa, key);

	EVP_PKEY_set1_RSA(pk, rsa); /* Also increments the rsa ref count */
	RSA_free(rsa); /* Drops our reference to it */
	return pk;
}

/* TODO: remove this function in libp11 0.5.0 */
int pkcs11_get_key_modulus(PKCS11_OBJECT_private *key, BIGNUM **bn)
{
	RSA *rsa = pkcs11_get1_rsa(key);
	const BIGNUM *rsa_n;

	if (!rsa)
		return 0;
#if OPENSSL_VERSION_NUMBER >= 0x10100005L || ( defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER >= 0x3050000fL )
	RSA_get0_key(rsa, &rsa_n, NULL, NULL);
#else
	rsa_n = rsa->n;
#endif
	*bn = BN_dup(rsa_n);

	RSA_free(rsa);

	return *bn == NULL ? 0 : 1;
}

/* TODO: remove this function in libp11 0.5.0 */
int pkcs11_get_key_exponent(PKCS11_OBJECT_private *key, BIGNUM **bn)
{
	RSA *rsa = pkcs11_get1_rsa(key);
	const BIGNUM *rsa_e;

	if (!rsa)
		return 0;
#if OPENSSL_VERSION_NUMBER >= 0x10100005L || ( defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER >= 0x3050000fL )
	RSA_get0_key(rsa, NULL, &rsa_e, NULL);
#else
	rsa_e = rsa->e;
#endif
	*bn = BN_dup(rsa_e);

	RSA_free(rsa);

	return *bn == NULL ? 0 : 1;
}

/* TODO: make this function static in libp11 0.5.0 */
int pkcs11_get_key_size(PKCS11_OBJECT_private *key)
{
	RSA *rsa = pkcs11_get1_rsa(key);
	int size;

	if (!rsa)
		return 0;

	size = RSA_size(rsa);

	RSA_free(rsa);

	return size;
}

#if ( ( defined (OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER < 0x10100005L ) || ( defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x3020199L ) )

int (*RSA_meth_get_priv_enc(const RSA_METHOD *meth))
		(int flen, const unsigned char *from,
			unsigned char *to, RSA *rsa, int padding)
{
	return meth->rsa_priv_enc;
}

int (*RSA_meth_get_priv_dec(const RSA_METHOD *meth))
		(int flen, const unsigned char *from,
			unsigned char *to, RSA *rsa, int padding)
{
	return meth->rsa_priv_dec;
}

static int (*RSA_meth_get_finish(const RSA_METHOD *meth)) (RSA *rsa)
{
	return meth->finish;
}

#endif

static int pkcs11_rsa_priv_dec_method(int flen, const unsigned char *from,
		unsigned char *to, RSA *rsa, int padding)
{
	PKCS11_OBJECT_private *key = pkcs11_get_ex_data_rsa(rsa);
	int (*priv_dec) (int flen, const unsigned char *from,
		unsigned char *to, RSA *rsa, int padding);

	if (check_object_fork(key) < 0) {
		priv_dec = RSA_meth_get_priv_dec(RSA_get_default_method());
		return priv_dec(flen, from, to, rsa, padding);
	}
	return pkcs11_private_decrypt(flen, from, to, key, padding);
}

static int pkcs11_rsa_priv_enc_method(int flen, const unsigned char *from,
		unsigned char *to, RSA *rsa, int padding)
{
	PKCS11_OBJECT_private *key = pkcs11_get_ex_data_rsa(rsa);
	int (*priv_enc) (int flen, const unsigned char *from,
		unsigned char *to, RSA *rsa, int padding);

	if (check_object_fork(key) < 0) {
		priv_enc = RSA_meth_get_priv_enc(RSA_get_default_method());
		return priv_enc(flen, from, to, rsa, padding);
	}
	return pkcs11_private_encrypt(flen, from, to, key, padding);
}

static int pkcs11_rsa_free_method(RSA *rsa)
{
	PKCS11_OBJECT_private *key = pkcs11_get_ex_data_rsa(rsa);
	int (*orig_rsa_free_method)(RSA *rsa) =
		RSA_meth_get_finish(RSA_get_default_method());

	if (key) {
		pkcs11_set_ex_data_rsa(rsa, NULL);
		pkcs11_object_free(key);
	}
	if (orig_rsa_free_method) {
		return orig_rsa_free_method(rsa);
	}
	return 1;
}

static void alloc_rsa_ex_index(void)
{
	if (rsa_ex_index == 0) {
		while (rsa_ex_index == 0) /* Workaround for OpenSSL RT3710 */
			rsa_ex_index = RSA_get_ex_new_index(0, "libp11 rsa",
				NULL, NULL, NULL);
		if (rsa_ex_index < 0)
			rsa_ex_index = 0; /* Fallback to app_data */
	}
}

static void free_rsa_ex_index(void)
{
	/* CRYPTO_free_ex_index requires OpenSSL version >= 1.1.0-pre1 */
#if OPENSSL_VERSION_NUMBER >= 0x10100001L && !defined(LIBRESSL_VERSION_NUMBER)
	if (rsa_ex_index > 0) {
		CRYPTO_free_ex_index(CRYPTO_EX_INDEX_RSA, rsa_ex_index);
		rsa_ex_index = 0;
	}
#endif
}

#if OPENSSL_VERSION_NUMBER < 0x10100005L || ( defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2080000L )

static RSA_METHOD *RSA_meth_dup(const RSA_METHOD *meth)
{
	RSA_METHOD *ret = OPENSSL_malloc(sizeof(RSA_METHOD));

	if (!ret)
		return NULL;
	memcpy(ret, meth, sizeof(RSA_METHOD));
	ret->name = OPENSSL_strdup(meth->name);
	if (!ret->name) {
		OPENSSL_free(ret);
		return NULL;
	}
	return ret;
}

static int RSA_meth_set1_name(RSA_METHOD *meth, const char *name)
{
	char *tmp = OPENSSL_strdup(name);

	if (!tmp)
		return 0;
	OPENSSL_free((char *)meth->name);
	meth->name = tmp;
	return 1;
}

#endif

#if OPENSSL_VERSION_NUMBER < 0x10100005L || ( defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x3000000L )

static int RSA_meth_set_flags(RSA_METHOD *meth, int flags)
{
	meth->flags = flags;
	return 1;
}
#endif

#if OPENSSL_VERSION_NUMBER < 0x10100005L || ( defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2080000L )

static int RSA_meth_set_priv_enc(RSA_METHOD *meth,
		int (*priv_enc) (int flen, const unsigned char *from,
		unsigned char *to, RSA *rsa, int padding))
{
	meth->rsa_priv_enc = priv_enc;
	return 1;
}

static int RSA_meth_set_priv_dec(RSA_METHOD *meth,
		int (*priv_dec) (int flen, const unsigned char *from,
		unsigned char *to, RSA *rsa, int padding))
{
	meth->rsa_priv_dec = priv_dec;
	return 1;
}

static int RSA_meth_set_finish(RSA_METHOD *meth, int (*finish)(RSA *rsa))
{
	meth->finish = finish;
	return 1;
}

#endif

/*
 * Overload the default OpenSSL methods for RSA
 */
RSA_METHOD *PKCS11_get_rsa_method(void)
{
	if (!pkcs11_rsa_method) {
		alloc_rsa_ex_index();
		pkcs11_rsa_method = RSA_meth_dup(RSA_get_default_method());
		if (!pkcs11_rsa_method)
			return NULL;
		RSA_meth_set1_name(pkcs11_rsa_method, "libp11 RSA method");
		RSA_meth_set_flags(pkcs11_rsa_method, 0);
		RSA_meth_set_priv_enc(pkcs11_rsa_method, pkcs11_rsa_priv_enc_method);
		RSA_meth_set_priv_dec(pkcs11_rsa_method, pkcs11_rsa_priv_dec_method);
		RSA_meth_set_finish(pkcs11_rsa_method, pkcs11_rsa_free_method);
	}
	return pkcs11_rsa_method;
}

void pkcs11_rsa_method_free(void)
{
	if (pkcs11_rsa_method) {
		free_rsa_ex_index();
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
		RSA_meth_free(pkcs11_rsa_method);
#else
		OPENSSL_free((char *)pkcs11_rsa_method->name);
		OPENSSL_free(pkcs11_rsa_method);
#endif
		pkcs11_rsa_method = NULL;
	}
}

PKCS11_OBJECT_ops pkcs11_rsa_ops = {
	EVP_PKEY_RSA,
	pkcs11_get_evp_key_rsa,
};

/* vim: set noexpandtab: */
