/* $Id: XmlEnc.c $ */

/* 	$Date: 2018-10-29 09:29:00 $
	$Revision: 1.0.1 $
*/

/******************************* LICENSE ***********************************
* Copyright (C) 2018 David Ireland, DI Management Services Pty Limited.
* All rights reserved. <www.di-mgt.com.au> <www.cryptosys.net>
* The code in this module is licensed under the terms of the MIT license.
* For a copy, see <http://opensource.org/licenses/MIT>
****************************************************************************
*/

/* THIS CODE DEMONSTRATES:
 * Using C programming language with CryptoSys PKI.
 * Reading private and public RSA keys from PXF/P12 data in memory.
 * Extracting X509 certificate from PFX data in memory.
 * Encrypting and decrypting using RSA-OAEP with SHA-256 digest and MGF options.
 * Base64 encoding and decoding.
 */

/*
Link to `diCrPKI.dll` in system directory using .lib file in current directory:
Properties > Linker > Input > AdditionalDependencies=.\diCrPKI.lib;%(AdditionalDependencies)
*/

#if _MSC_VER >= 1100
/* Detect memory leaks in MSVC++ */ 
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#else
#include <stdlib.h>
#endif
#ifdef NDEBUG
/* Make sure assertion testing is turned on */
#undef NDEBUG
#endif
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "diCrPKI.h"

/* typedef for BYTE type (uint8_t) */
typedef unsigned char BYTE;

/* INTERNAL UTILITIES */

/** Reads a binary file into a byte array
*  @return Pointer to allocated byte array or NULL on error
*  @remark Sets `*plen` with length of byte array
*  @remark Array is an exact copy of file bytes with _no_ terminating zero
*  @remark User must free allocated memory
*/
static BYTE *read_a_binary_file(const char *fname, long *plen) {
	FILE *fp;
	BYTE *buf;
	long flen;
	size_t nread;

	fp = fopen(fname, "rb");
	assert(fp != NULL);
	fseek(fp, 0, SEEK_END);
	flen = ftell(fp);
	assert(flen >= 0);
	buf = malloc(flen);
	rewind(fp);
	nread = fread(buf, 1, flen, fp);
	fclose(fp);
	*plen = (long)nread;

	return buf;
}


int main(void)
{
	const char *pfxname = "bob.pfx";	// Encrypted password = "password"
	const char *password = "password";	// high security!!
	const char *certname = "bob.cer"; // To be created
	// string values we will use below
	const char *begin_pem = "-----BEGIN PKCS12-----";
	const char *end_pem = "-----END PKCS12-----";
	BYTE *lpPfxData, *lpEncKey, *lpCT, *lpDecKey;
	char *tmp, *pfxstr;
	char *prikey, *pubkey;
	char *ciphervalue, *hexkey;
	long flen, nchars, slen, nbytes, clen, klen, dklen;
	// Our session key - we should generate at random each time, but for demo we use a fixed value
	BYTE sessionKey[] = {
		0x56, 0xBE, 0xEC, 0x5B, 0x9E, 0x40, 0xC9, 0xE1,
		0xBC, 0x42, 0xDE, 0x9C, 0x1F, 0x1A, 0x3D, 0x2F,
		0xF3, 0x87, 0xBE, 0xB0, 0x89, 0xB5, 0xDC, 0xA9,
		0xF0, 0x41, 0x79, 0xA6, 0x58, 0x36, 0x6B, 0xE2,
	};

	/* MSVC memory leak checking stuff */
#if _MSC_VER >= 1100
	_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
	_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
	_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
	_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
	_CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
	_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
	_CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
#endif

	printf("PKI version=%ld\n", PKI_Version(NULL, NULL));
	// Require at least PKI version 12.0
	assert(PKI_Version(NULL, NULL) >= 120000);

	/* Read in P12/PFX file to memory - in practice this would already exist */
	lpPfxData = read_a_binary_file(pfxname, &flen);
	assert(lpPfxData);
	printf("FILE: %s %ld bytes\n", pfxname, flen);

	/* This data is in binary form - first byte is 0x30 
	   If it's already in PEM format (begins with "-----BEGIN"), you can omit this step
	*/
	// Convert to base64 and add PEM encapsulation (messy in pure C!)
	nchars = CNV_B64StrFromBytes(NULL, 0, lpPfxData, flen);
	assert(nchars > 0);
	tmp = calloc(1, nchars + 1);	// NB +1
	nchars = CNV_B64StrFromBytes(tmp, nchars, lpPfxData, flen);
	slen = nchars + strlen(begin_pem) + strlen(end_pem) + 1;	// NB +1 here
	pfxstr = calloc(1, slen);	// slen is size of buffer incl term zero
	strcpy_s(pfxstr, slen, begin_pem);
	strcat_s(pfxstr, slen, tmp);
	strcat_s(pfxstr, slen, end_pem);
	printf("%s\n", pfxstr);
	/* Note that line breaks are not required in the PEM string, and we ignore the word "PKCS12" (other apps may not) 
	   We will just look for the strings "-----BEGIN" and "-----END" (exactly 5 dash characters in both cases)
	 */

	/* Now we have PFX in PEM format in a string, we can use it directly as a parameter in our functions */
	nchars = RSA_ReadAnyPrivateKey(NULL, 0, pfxstr, password, 0);
	assert(nchars > 0);
	prikey = malloc(nchars + 1);
	nchars = RSA_ReadAnyPrivateKey(prikey, nchars, pfxstr, password, 0);
	assert(nchars > 0);
	// quick debug
	printf("Private Key = %ld bits\n", RSA_KeyBits(prikey));

	/* We can get the public key (it's already included in the private key) */
	nchars = RSA_PublicKeyFromPrivate(NULL, 0, prikey, 0);
	assert(nchars > 0);
	pubkey = malloc(nchars + 1);
	nchars = RSA_PublicKeyFromPrivate(pubkey, nchars, prikey, 0);
	assert(nchars > 0);
	printf("Public Key = %ld bits\n", RSA_KeyBits(pubkey));

	/* And we can export the .CER file from inside the PFX data */
	nbytes = X509_GetCertFromPFX(certname, pfxstr, password, 0);
	assert(nbytes > 0);
	printf("Written %ld bytes to '%s'\n", nbytes, certname);

	/* Now lets encrypt our session key using RSA-OAEP - see manual for details about options
	https://www.cryptosys.net/pki/manpki/pki_RSA_Encrypt.html 
	We use the public key to encrypt

	<xenc:EncryptionMethod
	Algorithm="http://www.w3.org/2009/xmlenc11#rsa-oaep">
	<xenc11:MGF Algorithm="http://www.w3.org/2009/xmlenc11#mgf1sha256"/>
	<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
	</xenc:EncryptionMethod>

	PKI_EME_OAEP => use rsa-oaep
	PKI_HASH_SHA256 => use sha256 for the DigestAlgorithm AND mgf1sha256 for MGF Algorithm
	*/
	klen = sizeof(sessionKey);	// 256 bits/8 = 32 bytes
	nbytes = RSA_Encrypt(NULL, 0, sessionKey, klen, pubkey, "", PKI_EME_OAEP | PKI_HASH_SHA256);
	assert(nbytes > 0);
	// We must encrypt to an array of bytes first
	lpEncKey = malloc(nbytes);	// NB no extra here, it's a bytes array
	nbytes = RSA_Encrypt(lpEncKey, nbytes, sessionKey, klen, pubkey, "", PKI_EME_OAEP | PKI_HASH_SHA256);
	assert(nbytes > 0);
	/* Finally encode in base64 */
	nchars = CNV_B64StrFromBytes(NULL, 0, lpEncKey, nbytes);
	assert(nchars > 0);
	ciphervalue = malloc(nchars + 1);
	nchars = CNV_B64StrFromBytes(ciphervalue, nchars, lpEncKey, nbytes);

	/* Output as part of XML document - NB this value will be different each time */
	printf("<CipherValue>%s</CipherValue>\n", ciphervalue);

	/* PART 2 */
	/* We have (1) the CipherValue in a base64-encoded string (extracted from the XML)
	   (2) the private key (extracted from the PFX above)
	   - Let's decrypt
	*/

	/* Decode the base64 to a byte array (NB just base64, no XML markup here) */
	clen = CNV_BytesFromB64Str(NULL, 0, ciphervalue);
	assert(clen > 0);
	lpCT = malloc(clen);
	clen = CNV_BytesFromB64Str(lpCT, clen, ciphervalue);
	assert(clen > 0);

	/* Decrypt the byte array - you need to specify the parameters explicitly detected from the XML by other means */
	dklen = RSA_Decrypt(NULL, 0, lpCT, clen, prikey, "", "", PKI_EME_OAEP | PKI_HASH_SHA256);
	assert(dklen > 0);
	lpDecKey = calloc(1, dklen);
	dklen = RSA_Decrypt(lpDecKey, dklen, lpCT, clen, prikey, "", "", PKI_EME_OAEP | PKI_HASH_SHA256);
	assert(nbytes > 0);

	/* Display decrypted session key in hex */
	nchars = CNV_HexStrFromBytes(NULL, 0, lpDecKey, dklen);
	assert(nchars > 0);
	hexkey = malloc(nchars + 1);
	nchars = CNV_HexStrFromBytes(hexkey, nchars, lpDecKey, dklen);

	printf("SESSION KEY: 0x%s\n", hexkey);

	/* Free up memory*/
	free(lpPfxData);
	free(tmp);
	free(pfxstr);
	free(prikey);
	free(pubkey);
	free(lpEncKey);
	free(ciphervalue);
	free(lpCT);
	free(lpDecKey);
	free(hexkey);

	printf("\nALL DONE.\n");
	return 0;
}

/* EXPECTED FORM OF OUTPUT:
PKI version=120000
FILE: bob.pfx 1672 bytes
-----BEGIN PKCS12-----
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
CKQ68TJy9enBAgIIAA==
-----END PKCS12-----
Private Key = 1024 bits
Public Key = 1024 bits
Written 555 bytes to 'bob.cer'
<CipherValue>UyFrzuANad1RDSDJjZk4PJCUeh35CcPpFGhFjw90S3YY8HE3ZiIAZ82kyGtfSh+0ET
+EBtP01nzdzQulm/ZmqXDHk+0RWR3EeDy0z9wJbg2atHvaChjfPFXikBC1IZ1ZcWgT4GZlJt27bVWzm
UW9fnOH5Ke45AbZi4EVtpKgyuU=</CipherValue>
SESSION KEY: 0x56BEEC5B9E40C9E1BC42DE9C1F1A3D2FF387BEB089B5DCA9F04179A658366BE2

ALL DONE.
*/