/* $Id: EncryptBytesAndHex.c $ */

/* Examples showing how to encrypt a arbitrary-length 'text' string 
   with CryptoSys API using both 'Bytes' and `Hex' modes.
*/

/*
	Copyright (C) 2006 DI Management Services Pty Limited. 
	All rights reserved. <www.di-mgt.com.au> <www.cryptosys.net>
	Last updated:
	$Date: 2006-08-16 06:06:00 $
	Use at your own risk.
*/

/*
	NOTES:
	We use standard ANSI C here. Upgrade/downgrade to suit your own tastes.
	Because we are dealing with data of unknown length, we allocate memory as we go.
	This tends to obscure the main cryptography a bit. 
	We use assert statements to catch memory allocation problems and (shock! horror!)
	goto statements to handle errors and avoid memory leaks. 
	You should make your own improvements on this if you use it in production code.

	ENCRYPTION SCHEME.
	INPUT:  SZ, KY, IV
	OUTPUT: CT
	E.1. PT <- TOBYTES(SZ)	// convert `text' to plaintext bytes
	E.2. IB <- PT || PAD		// pad the PT to make an input block so |IB| mod n = 0
	E.3. CT <- ENCRYPT(KY, IV, IB)	// Encrypt the input block

	DECRYPTION SCHEME.
	INPUT:  CT, KY, IV
	OUTPUT: SZ
	D.1. OB <- DECRYPT(K, IV, CT)	// decrypt the ciphertext to an output block
	D.2a. PT || PAD <- OB	// decompose the OB into plaintext and padding
	D.2b. If PAD is invalid, return "DECRYPTION ERROR"
	D.3. SZ <- TOSTRING(PT)	// convert 'bytes' into 'text'

	NOTATION:
	KY: key
	IV: Initialization Vector
	SZ: a zero-terminated string containing text
	PT: plaintext bytes
	PAD: padding
	IB: input block to the encryption function
	CT: ciphertext bytes
	OB: output block from the decryption function
	|| denotes concatenation
	|x| denotes the length of x

	OTHER POINTS TO NOTE:
	Note how we carefully distinguish between 'text' and 'bytes'. 
	With ANSI characters in C you can get away with all sorts of fudging, but
	look what happens when we start using Unicode wide characters.
	'Text' is stored in a zero-terminated sequence of `char' or `wchar_t' types.
	A text string requires an extra character to be allocated for the zero terminating
	character and cannot contain a zero character inside it.
	'Bytes' are stored in an array of `unsigned char' types (often typedef'd as `BYTE').
	A byte array _can_ contain a zero byte inside it but always needs a separate variable
	to define its length.

	Because we are using CBC mode we have to pad the input before encryption. We use the
	PKCS7 method of padding and we _always_ add padding even if the original data is already
	a multiple of the block length. The padding can be used to detect a decryption error 
	but this is not 100% guaranteed.

	This example uses a hard-coded key which should _never_ be done in practice.
	In practice, too, a fresh IV should be generated at random each time a message is encrypted.
	The IV needs to be transmitted to the receiver along with the ciphertext. This can be done
	in the clear.

*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include "diCryptoSys.h"

/* Link to CryptoSys API library */
#pragma comment(lib, ".\\diCryptoSys.lib")

static void pr_bytesmsg(const char *msg, const unsigned char *bytes, int nbytes)
/* Print a message followed by hex-encoded byte array */
{
	int i;
	printf("%s", msg);
	for (i = 0; i < nbytes; i++)
		printf("%02X", bytes[i]);
	printf("\n");
}

void encrypt_with_bytes(void)
{
	/* Example encrypting plaintext string using Triple DES in CBC mode
	   passing data in `Byte' format */
	char szPlain[] = "Hello world!";
	unsigned char key[] = {
		0xF0, 0xE1, 0xD2, 0xC3, 0xB4, 0xA5, 0x96, 0x87, 
		0x78, 0x69, 0x5A, 0x4B, 0x3C, 0x2D, 0x1E, 0x0F, 
		0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
	};
	/* The IV should be generated at random each time */
	unsigned char iv[] = {
		0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
	};
	char *lpszCheck = NULL;
	unsigned char *lpPlain = NULL;
	unsigned char *lpBlock = NULL;
	unsigned char *lpCipher = NULL;
	long nPlain, nBlock, nCipher;
	long nRet;

	printf("USING `BYTE' PARAMETERS...\n");

	/* ENCRYPTION.
	   INPUT:	szPlain, a zero-terminated ANSI string of any length;
				key of exactly 24 bytes (to be kept secret);
				iv of exactly 8 bytes.
	   OUTPUT:	lpCipher, the ciphertext in an array of bytes;
				nCipher, the length of the ciphertext in bytes.
	*/

	/* E.1 Convert the plaintext string into byte format 
	   PT <- TOBYTES(SZ) */
	/* (In C we can just use a typecasted pointer) */
	lpPlain = (unsigned char *)szPlain;

	/* Compute its length in bytes - assumed `narrow' ANSI chars */
	nPlain = strlen(szPlain);

	pr_bytesmsg("KY=", key, sizeof(key));
	pr_bytesmsg("IV=", iv, sizeof(iv));
	printf("SZ='%s'\n", szPlain);
	pr_bytesmsg("PT=", lpPlain, nPlain);

	/* How long is the padded block? */
	nBlock = PAD_BytesBlock(NULL, 0, lpPlain, nPlain, API_BLK_TDEA_BYTES, 0);
	if (nBlock <= 0) return;

	/* Allocate storage for padded plaintext block */
	lpBlock = malloc(nBlock);
	assert(lpBlock);

	/* E.2 Pad the plaintext to an exact multiple of the block size */
	/* IB <- PT || PAD */
	nBlock = PAD_BytesBlock(lpBlock, nBlock, lpPlain, nPlain, API_BLK_TDEA_BYTES, 0);
	if (nBlock <= 0) return;
	pr_bytesmsg("IB=", lpBlock, nBlock);

	/* Allocate storage for ciphertext output = same as input block */
	lpCipher = malloc(nBlock);
	assert(lpCipher);

	/* E.3 Encrypt the padded input block using Triple DES in CBC mode */
	/* CT <- ENCRYPT(K, IV, IB) */
	nRet = TDEA_BytesMode(lpCipher, lpBlock, nBlock, key, ENCRYPT, "CBC", iv);
	/* Deal with error (e.g. invalid key length) */
	if (nRet != 0) goto clean_up;
	nCipher = nBlock;

	/* Return the result, CT */
	pr_bytesmsg("CT=", lpCipher, nCipher);

	free(lpBlock);

	/* END OF ENCRYPTION ... NOW WE DECRYPT ... */

	/* DECRYPTION.
	   INPUT:	lpCipher, the ciphertext in an array of bytes;
				nCipher, the length of the ciphertext in bytes;
				key; iv.
	   OUTPUT:	szPlain, a zero-terminated string.
	*/

	/* Allocate storage for padded decrypted plaintext block = same as ciphertext */
	nBlock = nCipher;
	lpBlock = malloc(nBlock);
	assert(lpBlock);

	/* D.1 Decrypt the ciphertext bytes 
	   OB <- DECRYPT(K, IV, CT) */
	nRet = TDEA_BytesMode(lpBlock, lpCipher, nCipher, key, DECRYPT, "CBC", iv);

	/* Deal with error */
	if (nRet != 0) goto clean_up;
	pr_bytesmsg("OB=", lpBlock, nBlock);

	/* D.2. Remove the padding bytes, or return an error if they are invalid 
	   PT || PAD <- OB */
	nPlain = PAD_UnpadBytes(lpBlock, nBlock, lpBlock, nBlock, API_BLK_TDEA_BYTES, 0);
	/* D.2b. If padding is invalid, return "DECRYPTION ERROR" */
	if (nPlain < 0) 
	{
		printf("DECRYPTION ERROR\n");
		goto clean_up;
	}

	/* D.3. Convert the new plaintext bytes into a string 
	   SZ <- TOSTRING(PT) */

	/* You could just do 
			lpBlock[nPlain] = '\0'; 
			lpszCheck = (char*)lpBlock;
	   but we create a separate string to emphasise the difference
	   between `bytes' and 'string' type.
	*/
	lpszCheck = malloc(nPlain + 1);
	memcpy(lpszCheck, lpBlock, nPlain);
	lpszCheck[nPlain] = '\0';

	/* Display the results */
	pr_bytesmsg("PT=", lpBlock, nPlain);
	printf("P'='%s'\n", lpszCheck);
	
clean_up:
	/* Note that lpPlain is not allocated */
	if (lpBlock) free(lpBlock);
	if (lpCipher) free(lpCipher);
	if (lpszCheck) free(lpszCheck);
}

void encrypt_with_hex(void)
{
	/* Same example encrypting plaintext string using Triple DES in CBC mode
	   using Hex encoded parameters */
	/*
	   INPUT:	szPlain, a zero-terminated ANSI string of any length;
				szKeyHex, hex-encoded key representing exactly 24 bytes;
				szIvHex, hex-encoded IV representing exactly 8 bytes.
	   OUTPUT:	lpszCipherHex, hex-encoded ciphertext string.
	*/
	char szPlain[]  = "Hello world!";
	char szKeyHex[] = "F0E1D2C3B4A5968778695A4B3C2D1E0F0011223344556677";
	char szIvHex[]  = "FEDCBA9876543210";

	char *lpszPlainHex = NULL;
	char *lpszBlockHex = NULL;
	char *lpszCipherHex = NULL;
	char *lpszCheck = NULL;
	long ptchars, blkchars;
	long nRet;

	printf("USING `HEX' PARAMETERS...\n");

	/* E.1. Encode the plaintext string into hex format 
	   PT <- HEX(TOBYTES(SZ)) */
	ptchars = CNV_HexStrFromBytes(NULL, 0, (unsigned char*)szPlain, strlen(szPlain));
	if (ptchars <= 0) return;
	lpszPlainHex = malloc(ptchars + 1);	/* NB extra one */
	ptchars = CNV_HexStrFromBytes(lpszPlainHex, ptchars, (unsigned char*)szPlain, strlen(szPlain));

	printf("KY=%s\n", szKeyHex);
	printf("IV=%s\n", szIvHex);
	printf("PT='%s'\n", szPlain);
	printf("PT=%s\n", lpszPlainHex);

	/* E.2. Pad the hex string directly, creating a new string 
	   IB <- PT || PAD */
	blkchars = PAD_HexBlock(NULL, 0, lpszPlainHex, API_BLK_TDEA_BYTES, 0);
	if (blkchars <= 0) return;
	lpszBlockHex = malloc(blkchars + 1);	/* NB extra one */
	assert(lpszBlockHex);
	blkchars = PAD_HexBlock(lpszBlockHex, blkchars, lpszPlainHex, API_BLK_TDEA_BYTES, 0);
	if (blkchars <= 0) goto clean_up;

	printf("IB=%s\n", lpszBlockHex);

	/* Allocate storage for output = same size as Input Block */
	lpszCipherHex = malloc(blkchars + 1);	/* NB extra one */
	if (NULL == lpszCipherHex) goto clean_up;

	/* E.3. Encrypt the plaintext data using Triple DES in CBC mode 
	   CT <- ENCRYPT(K, IV, IB) */
	nRet = TDEA_HexMode(lpszCipherHex, lpszBlockHex, szKeyHex, ENCRYPT, "CBC", szIvHex);
	/* Deal with error */
	if (nRet != 0) goto clean_up;

	/* Display the results */
	printf("CT=%s\n", lpszCipherHex);


	/* END OF ENCRYPTION ... NOW WE DECRYPT ... */

	/* DECRYPTION.
	   INPUT:	ciphertext in a hex-encoded string;
				key as a hex-encoded string; 
				iv used by sender as hex-encoded string.
	   OUTPUT:	szPlain, a zero-terminated string or "DECRYPTION ERROR".
	*/

	/* D.1 Decrypt the ciphertext: 
	   OB <- DECRYPT(K, IV, CT) */
	nRet = TDEA_HexMode(lpszBlockHex, lpszCipherHex, szKeyHex, DECRYPT, "CBC", szIvHex);
	/* Deal with error */
	if (nRet != 0) goto clean_up;
	printf("OB=%s\n", lpszBlockHex);

	/* D.2. Unpad the output block or return error if padding is invalid
	   PT || PAD <- OB
	   Note that the final output will ALWAYS be shorter than the block
	   so we can use the same memory */
	nRet = PAD_UnpadHex(lpszBlockHex, blkchars, lpszBlockHex, API_BLK_TDEA_BYTES, 0);
	if (nRet < 0) 
	{
		printf("DECRYPTION ERROR\n");
		goto clean_up;
	}

	/* D.3. Decode the unpadded plaintext hex into a zero-terminated string 
	   SZ <- TOSTRING(PT) */
	ptchars = CNV_BytesFromHexStr(NULL, 0, lpszBlockHex);
	lpszCheck = malloc(ptchars + 1);	/* NB one extra */
	assert(NULL != lpszCheck);
	ptchars = CNV_BytesFromHexStr(lpszCheck, ptchars, lpszBlockHex);
	lpszCheck[ptchars] = '\0';	/* Don't forget this */

	/* Display the results */
	printf("P'=%s\n", lpszBlockHex);
	printf("P'='%s'\n", lpszCheck);
	
clean_up:
	if (lpszPlainHex) free(lpszPlainHex);
	if (lpszBlockHex) free(lpszBlockHex);
	if (lpszCipherHex) free(lpszCipherHex);
}

void encrypt_wide_chars(void)
{
	/* To add some spice, let's do the `Bytes' method again 
	   but this time with the input as Unicode wide chars */
	wchar_t szPlain[] = L"Hello world!";
	unsigned char key[] = {
		0xF0, 0xE1, 0xD2, 0xC3, 0xB4, 0xA5, 0x96, 0x87, 
		0x78, 0x69, 0x5A, 0x4B, 0x3C, 0x2D, 0x1E, 0x0F, 
		0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
	};
	unsigned char iv[] = {
		0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
	};
	wchar_t *lpszCheck = NULL;
	unsigned char *lpPlain = NULL;
	unsigned char *lpBlock = NULL;
	unsigned char *lpCipher = NULL;
	long nPlain, nBlock, nCipher, nwchars;
	long nRet;

	printf("WITH WIDE CHARACTERS...\n");

	/* E.1 Convert the plaintext string into byte format 
	   PT <- TOBYTES(SZ) */
	/* This time, with wide chars, we can still use a cast for the byte array ptr
	   but we compute the length differently */
	lpPlain = (unsigned char *)szPlain;
	nwchars = wcslen(szPlain);
	nPlain = nwchars * sizeof(wchar_t);

	pr_bytesmsg("KY=", key, sizeof(key));
	pr_bytesmsg("IV=", iv, sizeof(iv));
	wprintf(L"SZ='%s'\n", szPlain);
	pr_bytesmsg("PT=", lpPlain, nPlain);

	/* How long is the padded block? */
	nBlock = PAD_BytesBlock(NULL, 0, lpPlain, nPlain, API_BLK_TDEA_BYTES, 0);
	if (nBlock <= 0) return;

	/* Allocate storage for padded plaintext block */
	lpBlock = malloc(nBlock);
	assert(lpBlock);

	/* E.2 Pad the plaintext to an exact multiple of the block size */
	/* IB <- PT || PAD */
	nBlock = PAD_BytesBlock(lpBlock, nBlock, lpPlain, nPlain, API_BLK_TDEA_BYTES, 0);
	if (nBlock <= 0) return;
	pr_bytesmsg("IB=", lpBlock, nBlock);

	/* Allocate storage for ciphertext output = same as input block */
	lpCipher = malloc(nBlock);
	assert(lpCipher);

	/* E.3 Encrypt the padded input block using Triple DES in CBC mode */
	/* CT <- ENCRYPT(K, IV, IB) */
	nRet = TDEA_BytesMode(lpCipher, lpBlock, nBlock, key, ENCRYPT, "CBC", iv);
	/* Deal with error (e.g. invalid key length) */
	if (nRet != 0) goto clean_up;
	nCipher = nBlock;

	/* Return the result, CT */
	pr_bytesmsg("CT=", lpCipher, nCipher);

	free(lpBlock);

	/* END OF ENCRYPTION ... NOW WE DECRYPT ... */

	/* Allocate storage for padded decrypted plaintext block = same as ciphertext */
	nBlock = nCipher;
	lpBlock = malloc(nBlock);
	assert(lpBlock);

	/* D.1 Decrypt the ciphertext bytes 
	   OB <- DECRYPT(K, IV, CT) */
	nRet = TDEA_BytesMode(lpBlock, lpCipher, nCipher, key, DECRYPT, "CBC", iv);

	/* Deal with error */
	if (nRet != 0) goto clean_up;
	pr_bytesmsg("OB=", lpBlock, nBlock);

	/* D.2. Remove the padding bytes, or return an error if they are invalid 
	   PT || PAD <- OB */
	nPlain = PAD_UnpadBytes(lpBlock, nBlock, lpBlock, nBlock, API_BLK_TDEA_BYTES, 0);
	/* D.2b. If padding is invalid, return "DECRYPTION ERROR" */
	if (nPlain < 0) 
	{
		printf("DECRYPTION ERROR\n");
		goto clean_up;
	}

	/* D.3. Convert the new plaintext bytes into a string 
	   SZ <- TOSTRING(PT) */
	/* Be careful with wide characters... */
	nwchars = nPlain / sizeof(wchar_t);
	lpszCheck = malloc((nwchars + 1) * sizeof(wchar_t));
	memcpy(lpszCheck, lpBlock, nPlain);
	lpszCheck[nwchars] = L'\0';

	/* Display the results */
	pr_bytesmsg("PT=", lpBlock, nPlain);
	wprintf(L"SZ='%s'\n", lpszCheck);
	
clean_up:
	/* Note that lpPlain is not allocated */
	if (lpBlock) free(lpBlock);
	if (lpCipher) free(lpCipher);
	if (lpszCheck) free(lpszCheck);
}


int main()
{
	encrypt_with_bytes();
	encrypt_with_hex();
	encrypt_wide_chars();
	return 0;
}