#include "module_openssl.h"

//#define  OPENSSL_API_COMPAT  30000

/// @brief  摘要算法 https://www.openssl.org/docs/manmaster/man3/EVP_DigestInit_ex2.html
/// @param  data  待加密数据
/// @param  dataLength  数据长度
/// @param  type    算法实现
/// @return 返回结果  长度大小最多64字节 EVP_MAX_MD_SIZE
CXBytes openssl_md(const char* data, UINT dataLength, const EVP_MD* type)
{
#if 0  //旧版写法
	MD5_CTX ctx;
	unsigned char outmd[16];
	int i = 0;

	memset(outmd, 0, sizeof(outmd));
	MD5_Init(&ctx);
	MD5_Update(&ctx, "hel", 3);
	MD5_Update(&ctx, "lo\n", 3);
	MD5_Final(outmd, &ctx);
	for (i = 0; i < 16; i < i++)
	{
		printf("%02X", outmd[i]);
	}
	printf("\n");

#endif

#if 10
	EVP_MD_CTX* mdctx = EVP_MD_CTX_new();
	if (NULL == mdctx)
		return CXBytes();

	CXBytes bytes;
	bytes.alloc(EVP_MAX_MD_SIZE);
	if (!EVP_DigestInit_ex2(mdctx, type, NULL)) //设置摘要算法和密码算法引擎，这里密码算法使用MD5，算法引擎使用OpenSSL默认引擎即软算法
	{
		EVP_MD_CTX_free(mdctx);
		return CXBytes();
	}
	if (!EVP_DigestUpdate(mdctx, data, dataLength))//调用摘要UpDate计算msg1的摘要
	{
		EVP_MD_CTX_free(mdctx);
		return CXBytes();
	}
	unsigned int outlen = 0;
	//	EVP_DigestUpdate(mdctx, msg2, strlen(msg2));//调用摘要UpDate计算msg2的摘要	
	if (!EVP_DigestFinal_ex(mdctx, (unsigned char*)bytes.get(), &outlen))//摘要结束，输出摘要值	
	{
		EVP_MD_CTX_free(mdctx);
		return CXBytes();
	}
	EVP_MD_CTX_free(mdctx);

	bytes.resize(outlen);

	BYTE* data_ = (BYTE*)bytes.get();
	printf("openssl_md:\n");
 	for (unsigned int i = 0; i < outlen; i++)
 	{
 		printf("0x%02x ", data_[i]);
 	}
 	printf("\n");
#endif
	return bytes;
}

//https://www.openssl.org/docs/manmaster/man3/DES_ecb_encrypt.html
/// @brief  
/// @param  data   加密或解密数据
/// @param  dataLen  数据长度,字节为单位
/// @param  key
/// @param  iv
/// @param  bEncrypt
/// @param  type
/// @return 如果成功返回TRUE,否则返回FALSE
CXBytes openssl_cipher(const char* data, UINT dataLen, const char* key, const char* iv, int bEncrypt, const EVP_CIPHER* type)
{
	EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
	if (NULL== ctx)
	{
		return CXBytes();
	}
	if (!EVP_CipherInit_ex2(ctx, type, NULL, NULL, bEncrypt ? 1 : 0, NULL))
	{
		EVP_CIPHER_CTX_free(ctx);
		return CXBytes();
	}
	int len1 = EVP_CIPHER_get_key_length(type);
	int len2 = EVP_CIPHER_get_iv_length(type);

//	OPENSSL_assert(EVP_CIPHER_CTX_get_key_length(ctx) == 16);
//	OPENSSL_assert(EVP_CIPHER_CTX_get_iv_length(ctx) == 16);

	CXBytes bytes;
	bytes.alloc(dataLen + EVP_MAX_BLOCK_LENGTH);
	 
	//unsigned char	outbuf[1024 + EVP_MAX_BLOCK_LENGTH] = {0};
	if (!EVP_CipherInit_ex2(ctx, type, (unsigned char*)key, (unsigned char*)iv, bEncrypt ? 1 : 0, NULL))
	{
		EVP_CIPHER_CTX_free(ctx);
		return CXBytes();
	}

	int outlen = 0;
	if (!EVP_CipherUpdate(ctx, (unsigned char*)bytes.get(), &outlen, (const unsigned char*)data, dataLen)) {
		EVP_CIPHER_CTX_free(ctx);
		return CXBytes();
	}
	int tmplen = 0;
	if (!EVP_CipherFinal_ex(ctx, (unsigned char*)bytes.get() + outlen, &tmplen)) {
		EVP_CIPHER_CTX_free(ctx);
		return CXBytes();
	}

	EVP_CIPHER_CTX_free(ctx);
	outlen += tmplen;
	bytes.resize(outlen);

	BYTE* data_ = (BYTE*)bytes.get();
	printf("openssl_cipher:\n");
	for (int i = 0; i < outlen; i++)
	{
		printf("0x%02x ", data_[i]);
	}
	printf("\n");
	return bytes;
}


//生成RSA密钥对,编码PEM格式
//https://www.openssl.org/docs/manmaster/man3/OSSL_ENCODER_to_bio.html
EVP_PKEY* openssl_rsa_keygen(int bits)
{
	EVP_PKEY_CTX* ctx;
	EVP_PKEY* pkey = NULL;

	ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
	if (NULL==ctx)
		return NULL; /* Error occurred */

	if (EVP_PKEY_keygen_init(ctx) <= 0)
	{
		EVP_PKEY_CTX_free(ctx);
		return NULL; /* Error */
	}
	if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, bits) <= 0)
	{
		EVP_PKEY_CTX_free(ctx);
		return NULL; /* Error */
	}
	/* Generate key */
	if (EVP_PKEY_keygen(ctx, &pkey) <= 0)
	{
		EVP_PKEY_CTX_free(ctx);
		return NULL; /* Error */
	}
	EVP_PKEY_CTX_free(ctx);
	return pkey;
}

CXTextA  openssl_rsa_key_print(EVP_PKEY* pKey, BOOL bPublic)
{
	CXTextA  text;
	BIO* bio_out = BIO_new(BIO_s_mem());
	if (bio_out)
	{
		if (bPublic)
			EVP_PKEY_print_public(bio_out, pKey, 0, NULL);
		else
			EVP_PKEY_print_private(bio_out, pKey, 0, NULL);

		char* pOut = NULL;
		long  len = BIO_get_mem_data(bio_out, &pOut);
		if (len > 0 && *pOut)
		{
			text.append2(pOut, len);
		}
		printf(text.get());
		printf("\n");
		BIO_free(bio_out);
	}
	return text;
}

CXTextA  openssl_rsa_key_pem(EVP_PKEY* pKey, BOOL bPublic,  const char* cipher_name, const char* pass)
{
	CXTextA  text;
	OSSL_ENCODER_CTX* ectx;
	if (bPublic)
	{
		const char* structure = "SubjectPublicKeyInfo";
		ectx = OSSL_ENCODER_CTX_new_for_pkey(pKey, OSSL_KEYMGMT_SELECT_PUBLIC_KEY, "PEM", structure, NULL);
	} else
	{
		const char* structure = "PrivateKeyInfo"; /* PKCS#8 structure */
		ectx = OSSL_ENCODER_CTX_new_for_pkey(pKey, OSSL_KEYMGMT_SELECT_PRIVATE_KEY, "PEM", structure, NULL);
	}
	if (cipher_name && *cipher_name)
		OSSL_ENCODER_CTX_set_cipher(ectx, cipher_name, NULL); //"AES-256-CBC"
	if (pass && *pass)
		OSSL_ENCODER_CTX_set_passphrase(ectx, (const unsigned char*)pass, strlen(pass)); //设置密码

	BIO* bio_out = BIO_new(BIO_s_mem());
	if (bio_out)
	{
		if (OSSL_ENCODER_to_bio(ectx, bio_out))
		{
			char* pOut = NULL;
			int   len = BIO_get_mem_data(bio_out, &pOut);
			if (len > 0 && *pOut)
			{
				text.append2(pOut, len);
			}
			printf(text.get());
			printf("\n");
		}
		BIO_free(bio_out);
	}
	OSSL_ENCODER_CTX_free(ectx);

	return text;
}

//https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_encrypt.html
CXBytes openssl_rsa_encrypt(EVP_PKEY* pKey, const char* data, UINT dataLen)
{
	CXBytes  bytes;
	EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new(pKey, NULL);
	if (NULL==ctx)
		return bytes;

	if (EVP_PKEY_encrypt_init(ctx) <= 0)
	{
		EVP_PKEY_CTX_free(ctx);
		return bytes;
	}
	if (EVP_PKEY_CTX_set_rsa_padding(ctx, 1) <= 0) //RSA_PKCS1_OAEP_PADDING
	{
		EVP_PKEY_CTX_free(ctx);
		return bytes;
	}

	/* Determine buffer length */
	size_t outlen=0;
	if (EVP_PKEY_encrypt(ctx, NULL, &outlen, (const unsigned char*)data, dataLen) <= 0)
	{
		EVP_PKEY_CTX_free(ctx);
		return bytes;
	}
	//	return NULL;/* malloc failure */
	bytes.alloc(outlen);

	if (EVP_PKEY_encrypt(ctx, (unsigned char*)bytes.get(), &outlen, (const unsigned char*)data, dataLen) <= 0)
	{
		EVP_PKEY_CTX_free(ctx);
		return CXBytes();
	}
	EVP_PKEY_CTX_free(ctx);
	/* Encrypted data is outlen bytes written to buffer out */
	printf("RSA加密成功\n");
	return bytes;
}



//https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_decrypt.html
CXBytes openssl_rsa_decrypt(EVP_PKEY* pKey, const char* data, UINT dataLen)
{
	CXBytes  bytes;
	EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new(pKey, NULL);
	if (NULL == ctx)
		return bytes;
	
	if (EVP_PKEY_verify_recover_init(ctx) <= 0)
	{
		EVP_PKEY_CTX_free(ctx);
		return bytes;
	}
	if (EVP_PKEY_CTX_set_rsa_padding(ctx, 1) <= 0)
	{
		EVP_PKEY_CTX_free(ctx);
		return bytes;
	}
	size_t outlen=0;
	/* Determine buffer length */
	if (EVP_PKEY_verify_recover(ctx, NULL, &outlen, (unsigned char*)data, dataLen) <= 0)
	{
		EVP_PKEY_CTX_free(ctx);
		return bytes;
	}
	bytes.alloc(outlen);
	if (EVP_PKEY_verify_recover(ctx, (unsigned char*)bytes.get(), &outlen, (unsigned char*)data, dataLen) <= 0)
	{
		EVP_PKEY_CTX_free(ctx);
		return CXBytes();
	}
	EVP_PKEY_CTX_free(ctx);
	bytes.resize(outlen);

	printf("RSA解码结果:%s\n", bytes.get());
	return bytes;
}


//从PEM 中获取 KEY
//https://www.openssl.org/docs/manmaster/man3/OSSL_DECODER_from_data.html
EVP_PKEY* openssl_rsa_pem_getkey(const char* data, UINT dataLen, BOOL bPublic, const char* passphrase)
{
	EVP_PKEY* pkey_pem = NULL;
	OSSL_DECODER_CTX* dctx = NULL;
	if(bPublic)
		dctx = OSSL_DECODER_CTX_new_for_pkey(&pkey_pem, "PEM", NULL, "RSA", OSSL_KEYMGMT_SELECT_PUBLIC_KEY, NULL, NULL);
	else
		dctx = OSSL_DECODER_CTX_new_for_pkey(&pkey_pem, "PEM", NULL, "RSA", OSSL_KEYMGMT_SELECT_PRIVATE_KEY, NULL, NULL);

	if (NULL == dctx)
	{
		if (pkey_pem) EVP_PKEY_free(pkey_pem);
		return NULL;
	}
	if (passphrase && *passphrase)
	{
		if (!OSSL_DECODER_CTX_set_passphrase(dctx, (const unsigned char*)passphrase, strlen(passphrase))) //设置密码
		{
			if(pkey_pem) EVP_PKEY_free(pkey_pem);
			OSSL_DECODER_CTX_free(dctx);
			return NULL;
		}
	}

	const char* data_ = data;
	size_t pdata_len = dataLen;
	if (!OSSL_DECODER_from_data(dctx, (const unsigned char**)(&data_), &pdata_len)) //返回后 data指向解码后的数据
	{
		if (pkey_pem) EVP_PKEY_free(pkey_pem);
		OSSL_DECODER_CTX_free(dctx);
		return NULL;
	}

	OSSL_DECODER_CTX_free(dctx);
	return pkey_pem;
}

//使用原始密钥数据创建 RSA 密钥对
//https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_fromdata.html
void test_RSA_KEY()
{
	/*
	 * These are extremely small to make this example simple.  A real
	 * and secure application will not use such small numbers.  A real
	 * and secure application is expected to use BIGNUMs, and to build
	 * this array dynamically.
	 */
	unsigned long rsa_n = 0xbc747fc5;
	unsigned long rsa_e = 0x10001;
	unsigned long rsa_d = 0x7b133399;
	OSSL_PARAM params[] = {
		OSSL_PARAM_ulong("n", &rsa_n),
		OSSL_PARAM_ulong("e", &rsa_e),
		OSSL_PARAM_ulong("d", &rsa_d),
		OSSL_PARAM_END
	};

	//EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new_from_name(libctx, "RSA", propq);
	EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
	EVP_PKEY* pkey = NULL;
	if (ctx == NULL || EVP_PKEY_fromdata_init(ctx) <= 0 || EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_KEYPAIR, params) <= 0)
	{
		EVP_PKEY_CTX_free(ctx);
		return;
	}
	BIO* bio_out = BIO_new(BIO_s_mem());
	EVP_PKEY_print_private(bio_out, pkey, 0, NULL);

	size_t outlen = BIO_ctrl_pending(bio_out);

	char* pp = NULL;
	long  len = BIO_get_mem_data(bio_out, &pp);
	printf((char*)pp);

	BIO_set_close(bio_out, BIO_NOCLOSE); /* So BIO_free() leaves BUF_MEM alone */
	BIO_free(bio_out);
	EVP_PKEY_CTX_free(ctx);

	printf("\n");
}
