/* libp11, a simple layer on to of PKCS#11 API
 * Copyright (C) 2017 Douglas E. Engert <deengert@gmail.com>
 * Copyright (C) 2017-2018 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
 */

#include "libp11-int.h"
#include <string.h>

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

#ifndef OPENSSL_NO_EC
static int (*orig_pkey_ec_sign_init) (EVP_PKEY_CTX *ctx);
static int (*orig_pkey_ec_sign) (EVP_PKEY_CTX *ctx,
	unsigned char *sig, size_t *siglen,
	const unsigned char *tbs, size_t tbslen);
#endif /* OPENSSL_NO_EC */

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

#if OPENSSL_VERSION_NUMBER >= 0x10000000L
#define EVP_PKEY_FLAG_DYNAMIC 1
#endif

#if OPENSSL_VERSION_NUMBER < 0x10002000L || defined(LIBRESSL_VERSION_NUMBER)

typedef struct {
	int nbits;
	BIGNUM *pub_exp;
	int gentmp[2];
	int pad_mode;
	const EVP_MD *md;
	const EVP_MD *mgf1md;
	int saltlen;
	unsigned char *tbuf;
} RSA_PKEY_CTX;

#endif

#if OPENSSL_VERSION_NUMBER < 0x10002000L || ( defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x3000100L )

static int EVP_PKEY_CTX_get_signature_md(EVP_PKEY_CTX *ctx, const EVP_MD **pmd)
{
	RSA_PKEY_CTX *rctx = EVP_PKEY_CTX_get_data(ctx);
	if (!rctx)
		return -1;
	*pmd = rctx->md;
	return 1;
}

#endif

#if OPENSSL_VERSION_NUMBER < 0x10002000L || ( defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x3010000L )

static int EVP_PKEY_CTX_get_rsa_oaep_md(EVP_PKEY_CTX *ctx, const EVP_MD **pmd)
{
	RSA_PKEY_CTX *rctx = EVP_PKEY_CTX_get_data(ctx);
	if (!rctx)
		return -1;
	*pmd = rctx->md;
	return 1;
}

#endif

#if OPENSSL_VERSION_NUMBER < 0x10001000L

static int EVP_PKEY_CTX_get_rsa_mgf1_md(EVP_PKEY_CTX *ctx, const EVP_MD **pmd)
{
	RSA_PKEY_CTX *rctx = EVP_PKEY_CTX_get_data(ctx);
	if (!rctx)
		return -1;
	*pmd = rctx->mgf1md;
	return 1;
}

static int EVP_PKEY_CTX_get_rsa_padding(EVP_PKEY_CTX *ctx, int *padding)
{
	RSA_PKEY_CTX *rctx = EVP_PKEY_CTX_get_data(ctx);
	if (!rctx)
		return -1;
	*padding = rctx->pad_mode;
	return 1;
}

static int EVP_PKEY_CTX_get_rsa_pss_saltlen(EVP_PKEY_CTX *ctx, int *saltlen)
{
	RSA_PKEY_CTX *rctx = EVP_PKEY_CTX_get_data(ctx);
	if (!rctx)
		return -1;
	*saltlen = rctx->saltlen;
	return 1;
}

static void EVP_PKEY_meth_copy(EVP_PKEY_METHOD *dst, const EVP_PKEY_METHOD *src)
{
	memcpy((int *)dst + 2, (int *)src + 2, 25 * sizeof(void (*)()));
}

#endif

#if OPENSSL_VERSION_NUMBER < 0x100020d0L || defined(LIBRESSL_VERSION_NUMBER)
static 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

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

static 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 !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_params_pss(CK_RSA_PKCS_PSS_PARAMS *pss,
		EVP_PKEY_CTX *ctx)
{
	const EVP_MD *sig_md, *mgf1_md;
	EVP_PKEY *evp_pkey;
	int salt_len;

	/* retrieve PSS parameters */
	if (EVP_PKEY_CTX_get_signature_md(ctx, &sig_md) <= 0)
		return -1;
	if (EVP_PKEY_CTX_get_rsa_mgf1_md(ctx, &mgf1_md) <= 0)
		return -1;
	if (!EVP_PKEY_CTX_get_rsa_pss_saltlen(ctx, &salt_len))
		return -1;
	switch (salt_len) {
	case -1:
		salt_len = EVP_MD_size(sig_md);
		break;
	case -2:
		evp_pkey = EVP_PKEY_CTX_get0_pkey(ctx);
		if (!evp_pkey)
			return -1;
		salt_len = EVP_PKEY_size(evp_pkey) - EVP_MD_size(sig_md) - 2;
		if (((EVP_PKEY_bits(evp_pkey) - 1) & 0x7) == 0)
			salt_len--;
		if (salt_len < 0) /* integer underflow detected */
			return -1;
	}
#ifdef DEBUG
	fprintf(stderr, "salt_len=%d sig_md=%s mdf1_md=%s\n",
		salt_len, EVP_MD_name(sig_md), EVP_MD_name(mgf1_md));
#endif

	/* fill the CK_RSA_PKCS_PSS_PARAMS structure */
	memset(pss, 0, sizeof(CK_RSA_PKCS_PSS_PARAMS));
	pss->hashAlg = pkcs11_md2ckm(sig_md);
	pss->mgf = pkcs11_md2ckg(mgf1_md);
	if (!pss->hashAlg || !pss->mgf)
		return -1;
	pss->sLen = salt_len;
	return 0;
}

static int pkcs11_params_oaep(CK_RSA_PKCS_OAEP_PARAMS *oaep,
		EVP_PKEY_CTX *ctx)
{
	const EVP_MD *oaep_md, *mgf1_md;

	/* retrieve OAEP parameters */
	if (EVP_PKEY_CTX_get_rsa_oaep_md(ctx, &oaep_md) <= 0)
		return -1;
	if (EVP_PKEY_CTX_get_rsa_mgf1_md(ctx, &mgf1_md) <= 0)
		return -1;
#ifdef DEBUG
	fprintf(stderr, "oaep_md=%s mdf1_md=%s\n",
		EVP_MD_name(oaep_md), EVP_MD_name(mgf1_md));
#endif

	/* fill the CK_RSA_PKCS_OAEP_PARAMS structure */
	memset(oaep, 0, sizeof(CK_RSA_PKCS_OAEP_PARAMS));
	oaep->hashAlg = pkcs11_md2ckm(oaep_md);
	oaep->mgf = pkcs11_md2ckg(mgf1_md);
	if (!oaep->hashAlg || !oaep->mgf)
		return -1;
	/* we do not support the OAEP "label" parameter yet... */
	oaep->source = CKZ_DATA_SPECIFIED;
	oaep->pSourceData = NULL; /* empty parameter (label) */
	oaep->ulSourceDataLen = 0;
	return 0;
}

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 rv = 0, padding;
	CK_ULONG size = *siglen;
	PKCS11_OBJECT_private *key;
	PKCS11_SLOT_private *slot;
	PKCS11_CTX_private *ctx;
	const EVP_MD *sig_md;
	CK_SESSION_HANDLE session;
	CK_MECHANISM mechanism;
	CK_RSA_PKCS_PSS_PARAMS pss_params;

#ifdef DEBUG
	fprintf(stderr, "%s:%d pkcs11_try_pkey_rsa_sign() "
		"sig=%p *siglen=%lu tbs=%p tbslen=%lu\n",
		__FILE__, __LINE__, sig, *siglen, tbs, tbslen);
#endif
	/* RSA method has EVP_PKEY_FLAG_AUTOARGLEN set. OpenSSL core will handle
	 * the size inquiry internally. */
	 if (!sig)
		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;
	ctx = slot->ctx;
	if (!evp_pkey_ctx)
		return -1;
	if (EVP_PKEY_CTX_get_signature_md(evp_pkey_ctx, &sig_md) <= 0)
		return -1;
	if (tbslen != (size_t)EVP_MD_size(sig_md))
		return -1;

	memset(&mechanism, 0, sizeof mechanism);
	EVP_PKEY_CTX_get_rsa_padding(evp_pkey_ctx, &padding);
	switch (padding) {
	case RSA_PKCS1_PSS_PADDING:
#ifdef DEBUG
		fprintf(stderr, "%s:%d padding=RSA_PKCS1_PSS_PADDING\n",
			__FILE__, __LINE__);
#endif
		if (pkcs11_params_pss(&pss_params, evp_pkey_ctx) < 0)
			return -1;
		mechanism.mechanism = CKM_RSA_PKCS_PSS;
		mechanism.pParameter = &pss_params;
		mechanism.ulParameterLen = sizeof pss_params;
		break;
	default:
#ifdef DEBUG
		fprintf(stderr, "%s:%d unsupported padding: %d\n",
			__FILE__, __LINE__, padding);
#endif
		return -1;
	} /* end switch(padding) */

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

	rv = CRYPTOKI_call(ctx,
		C_SignInit(session, &mechanism, key->object));
	if (!rv && key->always_authenticate == CK_TRUE)
		rv = pkcs11_authenticate(key, session);
	if (!rv)
		rv = CRYPTOKI_call(ctx,
			C_Sign(session, (CK_BYTE_PTR)tbs, tbslen, sig, &size));
	pkcs11_put_session(slot, session);
#ifdef DEBUG
	fprintf(stderr, "%s:%d C_SignInit or C_Sign rv=%d\n",
		__FILE__, __LINE__, rv);
#endif

	if (rv != CKR_OK)
		return -1;
	*siglen = size;
	return 1;
}

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_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 rv = 0, padding;
	CK_ULONG size = *outlen;
	PKCS11_OBJECT_private *key;
	PKCS11_SLOT_private *slot;
	PKCS11_CTX_private *ctx;
	CK_SESSION_HANDLE session;
	CK_MECHANISM mechanism;
	CK_RSA_PKCS_OAEP_PARAMS oaep_params;

#ifdef DEBUG
	fprintf(stderr, "%s:%d pkcs11_try_pkey_rsa_decrypt() "
		"out=%p *outlen=%lu in=%p inlen=%lu\n",
		__FILE__, __LINE__, out, *outlen, in, inlen);
#endif
	/* RSA method has EVP_PKEY_FLAG_AUTOARGLEN set. OpenSSL core will handle
	 * the size inquiry internally. */
	 if (!out)
		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;
	ctx = slot->ctx;

	if (!evp_pkey_ctx)
		return -1;

	memset(&mechanism, 0, sizeof mechanism);
	EVP_PKEY_CTX_get_rsa_padding(evp_pkey_ctx, &padding);
	switch (padding) {
	case RSA_PKCS1_OAEP_PADDING:
#ifdef DEBUG
		fprintf(stderr, "%s:%d padding=RSA_PKCS1_OAEP_PADDING\n",
			__FILE__, __LINE__);
#endif
		if (pkcs11_params_oaep(&oaep_params, evp_pkey_ctx) < 0)
			return -1;
		mechanism.mechanism = CKM_RSA_PKCS_OAEP;
		mechanism.pParameter = &oaep_params;
		mechanism.ulParameterLen = sizeof oaep_params;
		break;
	case RSA_PKCS1_PADDING:
#ifdef DEBUG
		fprintf(stderr, "%s:%d padding=RSA_PKCS1_PADDING\n",
			__FILE__, __LINE__);
#endif
		mechanism.mechanism = CKM_RSA_PKCS;
		mechanism.pParameter = NULL;
		mechanism.ulParameterLen = 0;
		break;
	default:
#ifdef DEBUG
		fprintf(stderr, "%s:%d unsupported padding: %d\n",
			__FILE__, __LINE__, padding);
#endif
		return -1;
	} /* end switch(padding) */

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

	rv = CRYPTOKI_call(ctx,
		C_DecryptInit(session, &mechanism, key->object));
	if (!rv && key->always_authenticate == CK_TRUE)
		rv = pkcs11_authenticate(key, session);
	if (!rv)
		rv = CRYPTOKI_call(ctx,
			C_Decrypt(session, (CK_BYTE_PTR)in, inlen, out, &size));
	pkcs11_put_session(slot, session);
#ifdef DEBUG
	fprintf(stderr, "%s:%d C_DecryptInit or C_Decrypt rv=%d\n",
		__FILE__, __LINE__, rv);
#endif

	if (rv != CKR_OK)
		return -1;
	*outlen = size;
	return 1;
}

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

static EVP_PKEY_METHOD *pkcs11_pkey_method_rsa()
{
	EVP_PKEY_METHOD *orig_meth, *new_meth;

	orig_meth = (EVP_PKEY_METHOD *)EVP_PKEY_meth_find(EVP_PKEY_RSA);
	EVP_PKEY_meth_get_sign(orig_meth,
		&orig_pkey_rsa_sign_init, &orig_pkey_rsa_sign);
	EVP_PKEY_meth_get_decrypt(orig_meth,
		&orig_pkey_rsa_decrypt_init,
		&orig_pkey_rsa_decrypt);

	new_meth = EVP_PKEY_meth_new(EVP_PKEY_RSA,
		EVP_PKEY_FLAG_AUTOARGLEN);

#ifdef EVP_PKEY_FLAG_DYNAMIC
	/* do not allow OpenSSL to free this object */
	new_meth->flags &= ~EVP_PKEY_FLAG_DYNAMIC;
#endif

	EVP_PKEY_meth_copy(new_meth, orig_meth);

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

	return new_meth;
}

#ifndef OPENSSL_NO_EC

static int pkcs11_try_pkey_ec_sign(EVP_PKEY_CTX *evp_pkey_ctx,
		unsigned char *sig, size_t *siglen,
		const unsigned char *tbs, size_t tbslen)
{
	EVP_PKEY *pkey;
	EC_KEY *eckey;
	int rv = CKR_GENERAL_ERROR;
	CK_ULONG size = *siglen;
	PKCS11_OBJECT_private *key;
	PKCS11_SLOT_private *slot;
	PKCS11_CTX_private *ctx;
	CK_SESSION_HANDLE session;
	const EVP_MD *sig_md;
	ECDSA_SIG *ossl_sig;
	CK_MECHANISM mechanism;

#ifdef DEBUG
	fprintf(stderr, "%s:%d pkcs11_try_pkey_ec_sign() "
		"sig=%p *siglen=%lu tbs=%p tbslen=%lu\n",
		__FILE__, __LINE__, sig, *siglen, tbs, tbslen);
#endif

	ossl_sig = ECDSA_SIG_new();
	if (!ossl_sig)
		goto error;

	pkey = EVP_PKEY_CTX_get0_pkey(evp_pkey_ctx);
	if (!pkey)
		goto error;

	eckey = (EC_KEY *)EVP_PKEY_get0_EC_KEY(pkey);
	if (!eckey)
		goto error;

	if (!sig) {
		*siglen = (size_t)ECDSA_size(eckey);
		rv = CKR_OK;
		goto error;
	}

	if (*siglen < (size_t)ECDSA_size(eckey))
		goto error;

	key = pkcs11_get_ex_data_ec(eckey);
	if (check_object_fork(key) < 0)
		goto error;

	slot = key->slot;
	ctx = slot->ctx;

	if (!evp_pkey_ctx)
		goto error;

	if (EVP_PKEY_CTX_get_signature_md(evp_pkey_ctx, &sig_md) <= 0)
		goto error;

	if (tbslen < (size_t)EVP_MD_size(sig_md))
		goto error;

	rv = 0;
	memset(&mechanism, 0, sizeof mechanism);
	mechanism.mechanism = CKM_ECDSA;

	if (pkcs11_get_session(slot, 0, &session))
		return -1;
	rv = CRYPTOKI_call(ctx,
		C_SignInit(session, &mechanism, key->object));
	if (!rv && key->always_authenticate == CK_TRUE)
		rv = pkcs11_authenticate(key, session);
	if (!rv)
		rv = CRYPTOKI_call(ctx,
			C_Sign(session, (CK_BYTE_PTR)tbs, tbslen, sig, &size));
	pkcs11_put_session(slot, session);

#ifdef DEBUG
	fprintf(stderr, "%s:%d C_SignInit or C_Sign rv=%d\n",
		__FILE__, __LINE__, rv);
#endif

	if (rv == CKR_OK) {
		BIGNUM *r = BN_bin2bn(sig, size/2, NULL);
		BIGNUM *s = BN_bin2bn(sig + size/2, size/2, NULL);

#if OPENSSL_VERSION_NUMBER >= 0x10100000L || ( defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER >= 0x3050000fL )
		ECDSA_SIG_set0(ossl_sig, r, s);
#else
		BN_free(ossl_sig->r);
		ossl_sig->r = r;
		BN_free(ossl_sig->s);
		ossl_sig->s = s;
#endif
		*siglen = i2d_ECDSA_SIG(ossl_sig, &sig);
	}

error:
	ECDSA_SIG_free(ossl_sig);

	if (rv != CKR_OK)
		return -1;

	return 1;
}

static int pkcs11_pkey_ec_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_ec_sign(evp_pkey_ctx, sig, siglen, tbs, tbslen);
	if (ret < 0)
		ret = (*orig_pkey_ec_sign)(evp_pkey_ctx, sig, siglen, tbs, tbslen);
	return ret;
}

static EVP_PKEY_METHOD *pkcs11_pkey_method_ec()
{
	EVP_PKEY_METHOD *orig_meth, *new_meth;

	orig_meth = (EVP_PKEY_METHOD *)EVP_PKEY_meth_find(EVP_PKEY_EC);
	EVP_PKEY_meth_get_sign(orig_meth,
		&orig_pkey_ec_sign_init, &orig_pkey_ec_sign);

	new_meth = EVP_PKEY_meth_new(EVP_PKEY_EC, 0);

#ifdef EVP_PKEY_FLAG_DYNAMIC
	/* do not allow OpenSSL to free this object */
	new_meth->flags &= ~EVP_PKEY_FLAG_DYNAMIC;
#endif

	EVP_PKEY_meth_copy(new_meth, orig_meth);

	EVP_PKEY_meth_set_sign(new_meth,
		orig_pkey_ec_sign_init, pkcs11_pkey_ec_sign);

	return new_meth;
}

#endif /* OPENSSL_NO_EC */

int PKCS11_pkey_meths(ENGINE *e, EVP_PKEY_METHOD **pmeth,
		const int **nids, int nid)
{
	static int pkey_nids[] = {
		EVP_PKEY_RSA,
		EVP_PKEY_EC,
		0
	};
	static EVP_PKEY_METHOD *pkey_method_rsa = NULL;
	static EVP_PKEY_METHOD *pkey_method_ec = NULL;

	(void)e; /* squash the unused parameter warning */
	/* all PKCS#11 engines currently share the same pkey_meths */

	if (!pmeth) { /* get the list of supported nids */
		*nids = pkey_nids;
		return sizeof(pkey_nids) / sizeof(int) - 1;
	}

	/* get the EVP_PKEY_METHOD */
	switch (nid) {
	case EVP_PKEY_RSA:
		if (!pkey_method_rsa)
			pkey_method_rsa = pkcs11_pkey_method_rsa();
		if (!pkey_method_rsa)
			return 0;
		*pmeth = pkey_method_rsa;
		return 1; /* success */
#ifndef OPENSSL_NO_EC
	case EVP_PKEY_EC:
		if (!pkey_method_ec)
			pkey_method_ec = pkcs11_pkey_method_ec();
		if (!pkey_method_ec)
			return 0;
		*pmeth = pkey_method_ec;
		return 1; /* success */
#endif /* OPENSSL_NO_EC */
	}
	*pmeth = NULL;
	return 0;
}

/* vim: set noexpandtab: */
