/*
 * p11test_case_pss_oaep.c: RSA-PSS and RSA-OAEP tests
 *
 * Copyright (C) 2017 Red Hat, Inc.
 *
 * Author: Jakub Jelen <jjelen@redhat.com>
 *
 * 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 General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "p11test_case_pss_oaep.h"
#include "libopensc/internal.h"

#include <openssl/sha.h>
#include <openssl/evp.h>

#define SHORT_MESSAGE_TO_SIGN "Simple message for signing & verifying. It needs to be little bit longer to fit also longer keys and allow the truncation.\n"
#define BUFFER_SIZE		4096

const unsigned char *global_message = (unsigned char *) SHORT_MESSAGE_TO_SIGN;
size_t global_message_length = sizeof(SHORT_MESSAGE_TO_SIGN);

const CK_MECHANISM_TYPE *
get_oaep_mechanism_hashes(CK_MECHANISM_TYPE mech)
{
	static CK_MECHANISM_TYPE h[6];

	switch (mech) {
	case CKM_RSA_PKCS_OAEP:
		h[0] = CKM_SHA_1;
		h[1] = CKM_SHA224;
		h[2] = CKM_SHA256;
		h[3] = CKM_SHA384;
		h[4] = CKM_SHA512;
		h[5] = -1;
		break;

	default:
		h[0] = -1;
		break;
	}

	return h;
}
const CK_MECHANISM_TYPE *
get_pss_mechanism_hashes(CK_MECHANISM_TYPE mech)
{
	static CK_MECHANISM_TYPE h[6];

	switch (mech) {
	case CKM_RSA_PKCS_PSS:
		h[0] = CKM_SHA_1;
		h[1] = CKM_SHA224;
		h[2] = CKM_SHA256;
		h[3] = CKM_SHA384;
		h[4] = CKM_SHA512;
		h[5] = -1;
		break;

	case CKM_SHA1_RSA_PKCS_PSS:
		h[0] = CKM_SHA_1;
		h[1] = -1;
		break;

	case CKM_SHA224_RSA_PKCS_PSS:
		h[0] = CKM_SHA224;
		h[1] = -1;
		break;

	case CKM_SHA256_RSA_PKCS_PSS:
		h[0] = CKM_SHA256;
		h[1] = -1;
		break;

	case CKM_SHA384_RSA_PKCS_PSS:
		h[0] = CKM_SHA384;
		h[1] = -1;
		break;

	case CKM_SHA512_RSA_PKCS_PSS:
		h[0] = CKM_SHA512;
		h[1] = -1;
		break;

	default:
		h[0] = -1;
		break;
	}

	return h;
}

const CK_MECHANISM_TYPE *
get_mechanism_hashes(CK_MECHANISM_TYPE mech)
{
	if (mech == CKM_RSA_PKCS_OAEP)
		return get_oaep_mechanism_hashes(mech);
	else
		return get_pss_mechanism_hashes(mech);
}

const CK_RSA_PKCS_MGF_TYPE *
get_mgfs(void)
{
	static CK_RSA_PKCS_MGF_TYPE h[6];
	h[0] = CKG_MGF1_SHA1;
	h[1] = CKG_MGF1_SHA224;
	h[2] = CKG_MGF1_SHA256;
	h[3] = CKG_MGF1_SHA384;
	h[4] = CKG_MGF1_SHA512;
	h[5] = -1;
	return h;
}

const EVP_MD *mgf_cryptoki_to_ossl(CK_RSA_PKCS_MGF_TYPE mgf)
{
	switch (mgf) {
	case CKG_MGF1_SHA224:
		return EVP_sha224();

	case CKG_MGF1_SHA256:
		return EVP_sha256();

	case CKG_MGF1_SHA384:
		return EVP_sha384();

	case CKG_MGF1_SHA512:
		return EVP_sha512();

	case CKG_MGF1_SHA1:
	default:
		return EVP_sha1();

	}
}

const EVP_MD *md_cryptoki_to_ossl(CK_MECHANISM_TYPE hash)
{
	/* Digest mechanisms */
	switch (hash) {
	case CKM_SHA224:
		return EVP_sha224();

	case CKM_SHA256:
		return EVP_sha256();

	case CKM_SHA384:
		return EVP_sha384();

	case CKM_SHA512:
		return EVP_sha512();

	case CKM_SHA_1:
	default:
		return EVP_sha1();

	}
}

size_t get_hash_length(CK_MECHANISM_TYPE mech)
{
	switch (mech) {
	case CKM_SHA224:
		return SHA224_DIGEST_LENGTH;
	case CKM_SHA256:
		return SHA256_DIGEST_LENGTH;
	case CKM_SHA384:
		return SHA384_DIGEST_LENGTH;
	case CKM_SHA512:
		return SHA512_DIGEST_LENGTH;
	default:
	case CKM_SHA_1:
		return SHA_DIGEST_LENGTH;
	}
}

CK_BYTE *hash_message(const CK_BYTE *message, size_t message_length,
    CK_MECHANISM_TYPE hash)
{
	CK_BYTE *out = NULL;
	const EVP_MD *md = NULL;
	size_t digest_len = 0;

	switch (hash) {
	case CKM_SHA224:
		digest_len = SHA224_DIGEST_LENGTH;
		md = EVP_sha224();
		break;

	case CKM_SHA256:
		digest_len = SHA256_DIGEST_LENGTH;
		md = EVP_sha256();
		break;

	case CKM_SHA384:
		digest_len = SHA384_DIGEST_LENGTH;
		md = EVP_sha384();
		break;

	case CKM_SHA512:
		digest_len = SHA512_DIGEST_LENGTH;
		md = EVP_sha512();
		break;

	case CKM_SHA_1:
	default:
		digest_len = SHA_DIGEST_LENGTH;
		md = EVP_sha1();
		break;
	}

	out = malloc(digest_len);
	if (!out ||
		EVP_Digest(message, message_length, out, NULL, md, NULL) != 1) {
		free(out);
		return NULL;
	}
	return out;
}

int oaep_encrypt_message_openssl(test_cert_t *o, token_info_t *info, CK_BYTE *message,
    CK_ULONG message_length, test_mech_t *mech, unsigned char **enc_message)
{
	size_t enc_length = 0;
	CK_RV rv = -1;
	EVP_PKEY_CTX *pctx = NULL;
	const EVP_MD *md = EVP_md_null();
	const EVP_MD *mgf1_md = EVP_md_null();

	md = md_cryptoki_to_ossl(mech->hash);
	mgf1_md = mgf_cryptoki_to_ossl(mech->mgf);

	if ((pctx = EVP_PKEY_CTX_new(o->key, NULL)) == NULL
		|| EVP_PKEY_encrypt_init(pctx) != 1
		|| EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_OAEP_PADDING) != 1
		|| EVP_PKEY_CTX_set_rsa_oaep_md(pctx, md) != 1
		|| EVP_PKEY_CTX_set_rsa_mgf1_md(pctx, mgf1_md) != 1) {
		fprintf(stderr, " [ ERROR %s ] Failed to initialize EVP_PKEY_CTX. Error: %s\n",
			o->id_str, ERR_error_string(ERR_peek_last_error(), NULL));
		goto out;
	}

	if (EVP_PKEY_encrypt(pctx, NULL, &enc_length, message, message_length) <= 0) {
		fprintf(stderr, " [ ERROR %s ] Failed get signature length. Error: %s\n",
			o->id_str, ERR_error_string(ERR_peek_last_error(), NULL));
		goto out;
	}

	*enc_message = OPENSSL_malloc(enc_length);

	rv = EVP_PKEY_encrypt(pctx, *enc_message, &enc_length, message, message_length);
	if (rv <= 0) {
		fprintf(stderr, " [ ERROR %s ] Signature is not valid. Error: %s\n",
			o->id_str, ERR_error_string(ERR_peek_last_error(), NULL));
	}
out:
	EVP_PKEY_CTX_free(pctx);
	return (int)enc_length;
}

void fill_oaep_params(CK_RSA_PKCS_OAEP_PARAMS *oaep_params,
    test_mech_t *mech)
{
	oaep_params->hashAlg = mech->hash;
	oaep_params->mgf = mech->mgf;
	oaep_params->source = CKZ_DATA_SPECIFIED;
	oaep_params->pSourceData = NULL;
	oaep_params->ulSourceDataLen = 0;

}

int oaep_encrypt_message(test_cert_t *o, token_info_t *info, CK_BYTE *message,
    CK_ULONG message_length, test_mech_t *mech, unsigned char **enc_message)
{
	CK_RV rv;
	CK_FUNCTION_LIST_PTR fp = info->function_pointer;
	CK_MECHANISM enc_mechanism = { mech->mech, NULL_PTR, 0 };
	CK_RSA_PKCS_OAEP_PARAMS oaep_params;
	CK_ULONG enc_message_length;
	static int encrypt_support = 1;

	fill_oaep_params(&oaep_params, mech);
	enc_mechanism.pParameter = &oaep_params;
	enc_mechanism.ulParameterLen = sizeof(oaep_params);

	if (!encrypt_support)
		goto openssl_encrypt;

	rv = fp->C_EncryptInit(info->session_handle, &enc_mechanism,
		o->public_handle);
	if (rv != CKR_OK) {
		debug_print("   C_EncryptInit: rv = 0x%.8lX", rv);
		encrypt_support = 0; /* avoid trying over and over again */
		goto openssl_encrypt;
	}

	/* get the expected length */
	rv = fp->C_Encrypt(info->session_handle, message, message_length,
	    NULL, &enc_message_length);
	if (rv != CKR_OK) {
		debug_print("   C_Encrypt: rv = 0x%.8lX", rv);
		goto openssl_encrypt;
	}
	*enc_message = malloc(enc_message_length);
	if (*enc_message == NULL) {
		debug_print("malloc returned null");
		return -1;
	}

	/* Do the actual encryption with allocated buffer */
	rv = fp->C_Encrypt(info->session_handle, message, message_length,
		*enc_message, &enc_message_length);
	if (rv == CKR_OK) {
		mech->result_flags |= FLAGS_DECRYPT_OPENSSL;
		return (int)enc_message_length;
	}
	debug_print("   C_Encrypt: rv = 0x%.8lX", rv);

openssl_encrypt:
	debug_print(" [ KEY %s ] Falling back to openssl encryption", o->id_str);
	return oaep_encrypt_message_openssl(o, info, message, message_length, mech,
	    enc_message);
}

int oaep_decrypt_message(test_cert_t *o, token_info_t *info, CK_BYTE *enc_message,
    CK_ULONG enc_message_length, test_mech_t *mech, unsigned char **dec_message)
{
	CK_RV rv;
	CK_FUNCTION_LIST_PTR fp = info->function_pointer;
	CK_MECHANISM dec_mechanism = { mech->mech, NULL_PTR, 0 };
	CK_RSA_PKCS_OAEP_PARAMS oaep_params;
	CK_ULONG dec_message_length = BUFFER_SIZE;

	fill_oaep_params(&oaep_params, mech);
	dec_mechanism.pParameter = &oaep_params;
	dec_mechanism.ulParameterLen = sizeof(oaep_params);

	rv = fp->C_DecryptInit(info->session_handle, &dec_mechanism,
		o->private_handle);
	if (rv == CKR_KEY_TYPE_INCONSISTENT) {
		debug_print(" [SKIP %s ] Not allowed to decrypt with this key?", o->id_str);
		return 0;
	} else if (rv != CKR_OK) {
		debug_print("  C_DecryptInit: rv = 0x%.8lX\n", rv);
		return -1;
	}

	*dec_message = malloc(dec_message_length);

	always_authenticate(o, info);

	rv = fp->C_Decrypt(info->session_handle, enc_message,
		enc_message_length, *dec_message, &dec_message_length);
	if (rv != CKR_OK) {
		free(*dec_message);
		debug_print("  C_Decrypt: rv = 0x%.8lX\n", rv);
		return -1;
	}
	return (int) dec_message_length;
}

/* Perform encryption and decryption of a message using private key referenced
 * in the  o  object with mechanism defined by  mech.
 *
 * NONE of the reasonable mechanisms support encryption/decryption
 *
 * Returns
 *  * 1 for successful Encrypt&Decrypt sequence
 *  * 0 for skipped test (unsupported mechanism, key, ...)
 *  * -1 otherwise.
 *  Serious errors terminate the execution.
 */
int oaep_encrypt_decrypt_test(test_cert_t *o, token_info_t *info, test_mech_t *mech)
{
	CK_BYTE *message = (CK_BYTE *) SHORT_MESSAGE_TO_SIGN;
	CK_BYTE *dec_message = NULL;
	int dec_message_length = 0;
	int message_length = 16;
	unsigned char *enc_message = NULL;
	int enc_message_length, rv;

	if (o->private_handle == CK_INVALID_HANDLE) {
		debug_print(" [SKIP %s ] Missing private key", o->id_str);
		return 0;
	}

	if (o->type != EVP_PKEY_RSA) {
		debug_print(" [ KEY %s ] Skip non-RSA key for encryption", o->id_str);
		return 0;
	}

	if (mech->mech != CKM_RSA_PKCS_OAEP) {
		mech->usage_flags &= ~CKF_DECRYPT;
		debug_print(" [SKIP %s ] non RSA-OAEP mechanism", o->id_str);
		return 0;
	}

	message_length = MIN((int)global_message_length,
		(int)((o->bits+7)/8 - 2*get_hash_length(mech->hash) - 2));

	/* will not work for 1024b RSA key and SHA512 hash: It has max size -2 */
	if (message_length < 0) {
		mech->usage_flags &= ~CKF_DECRYPT;
		debug_print(" [SKIP %s ] Too small modulus (%ld bits)"
			" or too large hash %s (%zu B) for OAEP", o->id_str,
			o->bits, get_mechanism_name(mech->hash),
			get_hash_length(mech->hash));
		return 0;
	}

	debug_print(" [ KEY %s ] Encrypt message of length %d using CKM_%s, "
		"hash CKM_%s, mgf=CKG_%s", o->id_str, (unsigned) message_length,
		get_mechanism_name(mech->mech), get_mechanism_name(mech->hash),
		get_mgf_name(mech->mgf));
	enc_message_length = oaep_encrypt_message(o, info, message,
		(unsigned) message_length, mech, &enc_message);
	if (enc_message_length <= 0) {
		return -1;
	}

	debug_print(" [ KEY %s ] Decrypt message", o->id_str);
	dec_message_length = oaep_decrypt_message(o, info, enc_message,
	    enc_message_length, mech, &dec_message);
	free(enc_message);
	if (dec_message_length <= 0) {
		return -1;
	}

	if (memcmp(dec_message, message, dec_message_length) == 0
			&& dec_message_length == message_length) {
		debug_print(" [  OK %s ] Text decrypted successfully.", o->id_str);
		mech->result_flags |= FLAGS_DECRYPT;
		rv = 1;
	} else {
		dec_message[dec_message_length] = '\0';
		debug_print(" [ ERROR %s ] Text decryption failed. Recovered text: %s",
			o->id_str, dec_message);
		rv = 0;
	}
	free(dec_message);
	return rv;
}

static unsigned long
get_max_salt_len(unsigned long bits, CK_MECHANISM_TYPE hash)
{
	return (bits + 7)/8 - get_hash_length(hash) - 2;
}

int fill_pss_params(CK_RSA_PKCS_PSS_PARAMS *pss_params,
    test_mech_t *mech, test_cert_t *o)
{
	pss_params->hashAlg = mech->hash;
	pss_params->mgf = mech->mgf;
	switch (mech->salt){
	case -2:
		/* max possible ( modlen - hashlen -2 ) */
		pss_params->sLen = get_max_salt_len(o->bits, mech->hash);
		break;
	case -1:
		/* digest length */
		/* will not work with SHA512 and 1024b keys (max is 62b!) */
		if (get_hash_length(mech->hash) > get_max_salt_len(o->bits, mech->hash)) {
			return -1;
		}
		pss_params->sLen = get_hash_length(mech->hash);
		break;
	case 0:
	default:
		pss_params->sLen = 0;
		break;
	}
	return 1;
}

int pss_sign_message(test_cert_t *o, token_info_t *info, CK_BYTE *message,
    CK_ULONG message_length, test_mech_t *mech, unsigned char **sign)
{
	CK_RV rv;
	CK_FUNCTION_LIST_PTR fp = info->function_pointer;
	CK_MECHANISM sign_mechanism = { mech->mech, NULL_PTR, 0 };
	CK_ULONG sign_length = 0;
	CK_RSA_PKCS_PSS_PARAMS pss_params;

	if (fill_pss_params(&pss_params, mech, o) != 1) {
		debug_print(" [SKIP %s ] Impossible to use requested salt length", o->id_str);
		return 0;
	}
	sign_mechanism.pParameter = &pss_params;
	sign_mechanism.ulParameterLen = sizeof(pss_params);

	rv = fp->C_SignInit(info->session_handle, &sign_mechanism,
		o->private_handle);
	if (rv == CKR_KEY_TYPE_INCONSISTENT) {
		debug_print(" [SKIP %s ] Not allowed to sign with this key?", o->id_str);
		return 0;
	} else if (rv == CKR_MECHANISM_INVALID) {
		debug_print(" [SKIP %s ] Bad mechanism. Not supported?", o->id_str);
		return 0;
	} else if (rv != CKR_OK) {
		debug_print("  C_SignInit: rv = 0x%.8lX\n", rv);
		return -1;
	}

	always_authenticate(o, info);

	/* Call C_Sign with NULL argument to find out the real size of signature */
	rv = fp->C_Sign(info->session_handle,
		message, message_length, *sign, &sign_length);
	if (rv != CKR_OK) {
		fprintf(stderr, "  C_Sign: rv = 0x%.8lX\n", rv);
		return -1;
	}

	*sign = malloc(sign_length);
	if (*sign == NULL) {
		fprintf(stderr, "%s: malloc failed", __func__);
		return -1;
	}

	/* Call C_Sign with allocated buffer to the actual signature */
	rv = fp->C_Sign(info->session_handle,
		message, message_length, *sign, &sign_length);

	if (rv != CKR_OK) {
		free(*sign);
		*sign = NULL;
		fprintf(stderr, "  C_Sign: rv = 0x%.8lX\n", rv);
		return -1;
	}
	return (int)sign_length;
}

int pss_verify_message_openssl(test_cert_t *o, token_info_t *info,
    CK_BYTE *message, CK_ULONG message_length, test_mech_t *mech,
    unsigned char *sign, CK_ULONG sign_length)
{
	int rv = -1;
	EVP_PKEY_CTX *pctx = NULL;
	const CK_BYTE *my_message;
	CK_BYTE *free_message = NULL;
	CK_ULONG my_message_length;
	const EVP_MD *mgf_md = EVP_md_null();
	const EVP_MD *md = EVP_md_null();

	md = md_cryptoki_to_ossl(mech->hash);
	mgf_md = mgf_cryptoki_to_ossl(mech->mgf);

	if (mech->mech != CKM_RSA_PKCS_PSS) {
		my_message = free_message = hash_message(message, message_length, mech->hash);
		my_message_length = get_hash_length(mech->hash);
	} else {
		my_message = message;
		my_message_length = message_length;
	}

	if ((pctx = EVP_PKEY_CTX_new(o->key, NULL)) == NULL
		|| EVP_PKEY_verify_init(pctx) != 1
		|| EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) != 1
		|| EVP_PKEY_CTX_set_signature_md(pctx, md) != 1
		|| EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, mech->salt) != 1
		|| EVP_PKEY_CTX_set_rsa_mgf1_md(pctx, mgf_md) != 1) {
		fprintf(stderr, " [ ERROR %s ] Failed to initialize EVP_PKEY_CTX. Error: %s\n",
			o->id_str, ERR_error_string(ERR_peek_last_error(), NULL));
		goto out;
	}

	rv = EVP_PKEY_verify(pctx, sign, sign_length, my_message, my_message_length);
	if (rv == 1) {
		debug_print(" [  OK %s ] Signature is valid.", o->id_str);
		mech->result_flags |= FLAGS_SIGN_OPENSSL;
	 } else {
		fprintf(stderr, " [ ERROR %s ] Signature is not valid. Error: %s\n",
			o->id_str, ERR_error_string(ERR_peek_last_error(), NULL));
		goto out;
	}
out:
	free(free_message);
	EVP_PKEY_CTX_free(pctx);
	return rv;
}

int pss_verify_message(test_cert_t *o, token_info_t *info, CK_BYTE *message,
    CK_ULONG message_length, test_mech_t *mech, unsigned char *sign,
    CK_ULONG sign_length)
{
	CK_RV rv;
	CK_FUNCTION_LIST_PTR fp = info->function_pointer;
	CK_MECHANISM sign_mechanism = { mech->mech, NULL_PTR, 0 };
	CK_RSA_PKCS_PSS_PARAMS pss_params;
	static int verify_support = 1;

	if (!verify_support)
		goto openssl_verify;

	fill_pss_params(&pss_params, mech, o);
	sign_mechanism.pParameter = &pss_params;
	sign_mechanism.ulParameterLen = sizeof(pss_params);

	/* try C_Verify() if it is supported */
	rv = fp->C_VerifyInit(info->session_handle, &sign_mechanism,
		o->public_handle);
	if (rv != CKR_OK) {
		debug_print("   C_VerifyInit: rv = 0x%.8lX", rv);
		verify_support = 0; /* avoid trying over and over again */
		goto openssl_verify;
	}

	rv = fp->C_Verify(info->session_handle,
		message, message_length, sign, sign_length);

	if (rv == CKR_OK) {
		mech->result_flags |= FLAGS_SIGN;
		debug_print(" [  OK %s ] Verification successful", o->id_str);
		return 1;
	}
	debug_print("   C_Verify: rv = 0x%.8lX", rv);
	verify_support = 0; /* avoid trying over and over again */

openssl_verify:
	debug_print(" [ KEY %s ] Falling back to openssl verification", o->id_str);
	return pss_verify_message_openssl(o, info, message, message_length, mech,
		sign, sign_length);
}

/* Perform signature and verification of a message using private key referenced
 * in the  o  object with mechanism defined by  mech.
 *
 * Returns
 *  * 1 for successful Sign&Verify sequence
 *  * 0 for skipped test (unsupported mechanism, key, ...)
 *  * -1 otherwise.
 *  Serious errors terminate the execution.
 */
int pss_sign_verify_test(test_cert_t *o, token_info_t *info, test_mech_t *mech)
{
	CK_BYTE *message = NULL;
	size_t message_length = global_message_length;
	CK_BYTE *sign = NULL;
	CK_ULONG sign_length = 0;
	int rv = 0;

	if (o->private_handle == CK_INVALID_HANDLE) {
		debug_print(" [SKIP %s ] Missing private key", o->id_str);
		return 0;
	}

	if (o->type != EVP_PKEY_RSA) {
		debug_print(" [SKIP %s ] Skip non-RSA key", o->id_str);
		return 0;
	}

	if (!is_pss_mechanism(mech->mech)) {
		mech->usage_flags &= ~CKF_SIGN;
		debug_print(" [SKIP %s ] non RSA-PSS mechanism %s", o->id_str,
			get_mechanism_name(mech->mech));
		return 0;
	}

	if (mech->mech == CKM_RSA_PKCS_PSS) {
		message = hash_message(global_message, global_message_length,
			mech->hash);
		message_length = get_hash_length(mech->hash);
	} else {
		message = (unsigned char *) SHORT_MESSAGE_TO_SIGN;
	}

	debug_print(" [ KEY %s ] Signing message using CKM_%s, CKM_%s,"
		" CKG_%s, salt_len=%d", o->id_str,
		get_mechanism_name(mech->mech), get_mechanism_name(mech->hash),
		get_mgf_name(mech->mgf), mech->salt);
	rv = pss_sign_message(o, info, message, message_length, mech, &sign);
	if (rv <= 0) {
		goto out;
	}
	sign_length = (unsigned long) rv;

	debug_print(" [ KEY %s ] Verify message signature", o->id_str);
	rv = pss_verify_message(o, info, message, message_length, mech,
		sign, sign_length);
out:
	if (mech->mech == CKM_RSA_PKCS_PSS) {
		free(message);
	}
	free(sign);
	return rv;
}

/* ignore the prefilled mechanisms and list all combinations of mechanisms
 * found, all reasonable hash functions, MGFs and salt lengths
 */
void fill_object_pss_mechanisms(token_info_t *info, test_cert_t *o)
{
	const CK_MECHANISM_TYPE *h;
	const CK_RSA_PKCS_MGF_TYPE *mgf;
	int n = 0, s;
	unsigned int j;

	for (j = 0; j < token.num_rsa_mechs; j++) {
		test_mech_t *source_mech = &token.rsa_mechs[j];

		/* skip non-RSA-PSS mechs early */
		if (!is_pss_mechanism(source_mech->mech) &&
			source_mech->mech != CKM_RSA_PKCS_OAEP) {
			continue;
		}

		h = get_mechanism_hashes(source_mech->mech);
		for (; *h != (CK_MECHANISM_TYPE) -1; h++) {
			mgf = get_mgfs();
			for (; *mgf != (CK_RSA_PKCS_MGF_TYPE) -1; mgf++) {
				/* OAEP does not have salt */
				if (source_mech->mech == CKM_RSA_PKCS_OAEP)
					s = 0;
				else
					s = -2;

				for (; s <= 0; s++) {
					test_mech_t *mech = &o->mechs[n++];
					mech->mech = source_mech->mech;
					mech->hash = *h;
					mech->mgf = *mgf;
					mech->salt = s;
					mech->usage_flags =
						source_mech->usage_flags;
					mech->result_flags = 0;
					if (n >= MAX_MECHS)
						P11TEST_FAIL(info,
							"Too many mechanisms (%d)",
							MAX_MECHS);
				}
			}
		}

	}
	o->num_mechs = n;
}

int have_pss_oaep_mechanisms()
{
	unsigned have = 0, i;
	for (i = 0; i <= token.num_rsa_mechs; i++) {
		if (is_pss_mechanism(token.rsa_mechs[i].mech) ||
			token.rsa_mechs[i].mech == CKM_RSA_PKCS_OAEP) {
				have++;
		}
	}
	return have;
}

void pss_oaep_test(void **state) {

	token_info_t *info = (token_info_t *) *state;
	unsigned int i;
	int used;
	size_t j;
	test_certs_t objects;

	test_certs_init(&objects);

	P11TEST_START(info);

	if (have_pss_oaep_mechanisms() == 0) {
		fprintf(stderr, "Token does not support any RSA-PSS or OAEP mechanisms. Skipping.\n");
		skip();
	}

	search_for_all_objects(&objects, info);

	debug_print("\nCheck functionality of Sign&Verify and/or Encrypt&Decrypt with RSA/OAEP mechanisms");
	for (i = 0; i < objects.count; i++) {
		test_cert_t *o = &objects.data[i];
		/* do the Sign&Verify and/or Encrypt&Decrypt */
		used = 0;
		if (o->private_handle == CK_INVALID_HANDLE) {
			debug_print(" [SKIP %s ] Missing private key",
				o->id_str);
			continue;
		}
		/* Do not list non-RSA keys here */
		if (o->type != EVP_PKEY_RSA)
			continue;

		fill_object_pss_mechanisms(info, o);
		for (j = 0; j < o->num_mechs; j++)
			if (o->mechs[j].mech != CKM_RSA_PKCS_OAEP)
				used |= pss_sign_verify_test(o, info,
					&(o->mechs[j]));

		for (j = 0; j < o->num_mechs; j++)
			if (o->mechs[j].mech == CKM_RSA_PKCS_OAEP)
				used |= oaep_encrypt_decrypt_test(o, info,
					&(o->mechs[j]));

		if (!used) {
			debug_print(" [ WARN %s ] Private key with unknown purpose T:%02lX",
			o->id_str, o->key_type);
		}
	}

	if (objects.count == 0) {
		printf(" [WARN] No objects to display\n");
		return;
	}

	/* print summary */
	printf("[KEY ID] [LABEL]\n");
	printf("[ TYPE ] [ SIZE ] [PUBLIC]                               [SIGN&VERIFY] [ENC&DECRYPT]\n");
	printf("[ MECHANISM              ] [ HASH ] [    MGF    ] [SALT] [   WORKS   ] [   WORKS   ]\n");
	P11TEST_DATA_ROW(info, 7,
		's', "KEY ID",
		's', "MECHANISM",
		's', "HASH",
		's', "MGF",
		's', "SALT",
		's', "SIGN&VERIFY WORKS",
		's', "ENCRYPT&DECRYPT WORKS");
	for (i = 0; i < objects.count; i++) {
		test_cert_t *o = &objects.data[i];

		/* Do not go through incomplete pairs */
		if (o->private_handle == CK_INVALID_HANDLE)
			continue;

		/* Do not list non-RSA keys here */
		if (o->type != EVP_PKEY_RSA)
			continue;

		printf("\n[%-6s] [%s]\n",
			o->id_str,
			o->label);
		printf("[ %s ] [%6lu] [ %s ]                               [%s%s] [%s%s]\n",
			o->key_type == CKK_RSA ? "RSA " :
				o->key_type == CKK_EC ? " EC " : " ?? ",
			o->bits,
			o->verify_public == 1 ? " ./ " : "    ",
			o->sign ? "[./] " : "[  ] ",
			o->verify ? " [./] " : " [  ] ",
			o->encrypt ? "[./] " : "[  ] ",
			o->decrypt ? " [./] " : " [  ] ");
		if (!o->sign && !o->verify && !o->encrypt && !o->decrypt) {
			printf("  no usable attributes found ... ignored\n");
			continue;
		}
		for (j = 0; j < o->num_mechs; j++) {
			test_mech_t *mech = &o->mechs[j];
			printf("  [ %-20s ] [%-6s] [%-11s] [%4d] [   %s    ] [   %s    ]\n",
				get_mechanism_name(mech->mech),
				get_mechanism_name(mech->hash),
				get_mgf_name(mech->mgf),
				mech->salt,
				mech->result_flags & FLAGS_SIGN_ANY
				? "[./]" : "    ",
				mech->result_flags & FLAGS_DECRYPT_ANY
				? "[./]" : "    ");
			if ((mech->result_flags & FLAGS_SIGN_ANY) == 0 &&
				(mech->result_flags & FLAGS_DECRYPT_ANY) == 0)
				continue; /* skip empty rows for export */
			P11TEST_DATA_ROW(info, 7,
				's', o->id_str,
				's', get_mechanism_name(mech->mech),
				's', get_mechanism_name(mech->hash),
				's', get_mgf_name(mech->mgf),
				'd', mech->salt,
				's', mech->result_flags & FLAGS_SIGN_ANY
				? "YES" : "",
				's', mech->result_flags & FLAGS_DECRYPT_ANY
				? "YES" : "");
		}
	}
	printf(" Public == Cert ----------^                                ^  ^  ^       ^  ^  ^\n");
	printf(" Sign Attribute -------------------------------------------'  |  |       |  |  |\n");
	printf(" Sign&Verify functionality -----------------------------------'  |       |  |  |\n");
	printf(" Verify Attribute -----------------------------------------------'       |  |  |\n");
	printf(" Encrypt Attribute ------------------------------------------------------'  |  |\n");
	printf(" Encrypt & Decrypt functionality -------------------------------------------'  |\n");
	printf(" Decrypt Attribute ------------------------------------------------------------'\n");

	clean_all_objects(&objects);
	P11TEST_PASS(info);
}
