#include "vk_constants.h"
#include "vk_error.h"
#include "vk_log.h"
#include "crypto/vk_crypto_rsa.h"
#include "openssl/evp.h"
#include "openssl/x509.h"

int vk_crypto_rsa_verify_signature(unsigned char* signature, unsigned int signature_len,
									unsigned char* md, unsigned int md_len, unsigned char* cert,
									unsigned int cert_len)
{
	X509* pServerCert = NULL;
	EVP_PKEY* pkey = NULL;
	EVP_MD_CTX digest_ctx;
	EVP_PKEY_CTX* pkey_ctx;
	int ret = VK_ERR_GENERAL;

	if (cert == NULL) {
		LOGE("%s: cert is null\n", __func__);
		return VK_ERR_INVALID_ARGUMENT;
	}

	if (cert_len <= 0) {
		LOGE("%s: wrong cert_len(%d)\n", __func__, cert_len);
		return VK_ERR_INVALID_ARGUMENT;
	}

	if (signature == NULL) {
		LOGE("%s: signature is null\n", __func__);
		return VK_ERR_INVALID_ARGUMENT;
	}

	if (signature_len <= 0) {
		LOGE("%s: sig len is wrong(%d)\n", __func__, signature_len);
		return VK_ERR_INVALID_ARGUMENT;
	}

	if (md == NULL) {
		LOGE("%s: md is null\n", __func__);
		return VK_ERR_INVALID_ARGUMENT;
	}

	if (md_len <= 0) {
		LOGE("%s: md_len is wrong(%d)\n", __func__, md_len);
		return VK_ERR_INVALID_ARGUMENT;
	}

	EVP_MD_CTX_init(&digest_ctx);
	// Read server cert.
	if ((pServerCert = d2i_X509(NULL, (const unsigned char**)&cert, cert_len)) == NULL) {
		LOGE("%s: Failed to read server cert", __func__);
		ret = VK_ERR_VERIFY_SIGNATURE;
		goto out;
	}

	// Read server cert's public key.
	if ((pkey = X509_get_pubkey(pServerCert)) == NULL) {
		LOGE("%s: Failed to read server pubkey", __func__);
		ret = VK_ERR_VERIFY_SIGNATURE;
		goto out;
	}

	if (!EVP_DigestVerifyInit(&digest_ctx, &pkey_ctx, EVP_sha256(), NULL, pkey)) {
		LOGE("%s: Failed EVP_DigestVerifyInit\n", __func__);
		ret = VK_ERR_VERIFY_SIGNATURE;
		goto out;
	}

	if (!EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING)) {
		LOGE("%s: Failed EVP_PKEY_CTX_set_rsa_padding\n", __func__);
		ret = VK_ERR_VERIFY_SIGNATURE;
		goto out;
	}

	if (!EVP_DigestVerifyUpdate(&digest_ctx, md, md_len)) {
		LOGE("%s: Failed EVP_DigestVerifyUpdate\n", __func__);
		ret = VK_ERR_VERIFY_SIGNATURE;
		goto out;
	}

	if (!EVP_DigestVerifyFinal(&digest_ctx, signature, signature_len)) {
		LOGE("%s: Failed EVP_DigestVerifyFinal\n", __func__);
		ret = VK_ERR_VERIFY_SIGNATURE;
		goto out;
	}

	ret = VK_SUCCESS;
out:
	EVP_MD_CTX_cleanup(&digest_ctx);

	if (pkey) {
		EVP_PKEY_free(pkey);
		pkey = NULL;
	}

	if (pServerCert) {
		X509_free(pServerCert);
		pServerCert = NULL;
	}

	return ret;
}
