#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;
	EVP_MD_CTX digest_ctx;
	EVP_PKEY_CTX* pkey_ctx;

	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;
	}

	// Read server cert.
	if ((pServerCert = d2i_X509(NULL, (const unsigned char**)&cert, cert_len)) == NULL) {
		LOGE("%s: Failed to read server cert", __func__);
		return VK_ERR_VERIFY_SIGNATURE;
	}

	// Read server cert's public key.
	if ((pkey = X509_get_pubkey(pServerCert)) == NULL) {
		LOGE("%s: Failed to read server pubkey", __func__);
		return VK_ERR_VERIFY_SIGNATURE;
	}

	EVP_MD_CTX_init(&digest_ctx);

	if (!EVP_DigestVerifyInit(&digest_ctx, &pkey_ctx, EVP_sha256(), NULL, pkey)) {
		LOGE("%s: Failed EVP_DigestVerifyInit\n", __func__);
		return VK_ERR_VERIFY_SIGNATURE;
	}

	if (!EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING)) {
		LOGE("%s: Failed EVP_PKEY_CTX_set_rsa_padding\n", __func__);
		return VK_ERR_VERIFY_SIGNATURE;
	}

	if (!EVP_DigestVerifyUpdate(&digest_ctx, md, md_len)) {
		LOGE("%s: Failed EVP_DigestVerifyUpdate\n", __func__);
		return VK_ERR_VERIFY_SIGNATURE;
	}

	if (!EVP_DigestVerifyFinal(&digest_ctx, signature, signature_len)) {
		LOGE("%s: Failed EVP_DigestVerifyFinal\n", __func__);
		return VK_ERR_VERIFY_SIGNATURE;
	}

	EVP_MD_CTX_cleanup(&digest_ctx);

	return VK_SUCCESS;
}
