/* $Id: TestPKI.c $ */

// Formerly PKI_Examples.c

/* Some tests using the CryptoSys PKI Pro C/C++ interface.
* Please report any bugs to <https://cryptosys.net/contact/>
*/

/*
Last updated:
	$Date: 2023-01-01 03:19:00 $
	$Revision: 21.0.0 $
*/
/******************************* LICENSE ***********************************
* Copyright (C) 2004-23 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,
* unless otherwise marked.
* For a copy, see <http://opensource.org/licenses/MIT>
****************************************************************************
*/

/*
COMPILING
---------
WINDOWS: link to the library file `diCrPKi.lib`.
Requires `diCrPKI.dll` to be in your library search path.

LINUX: link to the dynamic library `libcryptosyspki.so`
    gcc -g -Wall pki_check.c -lcryptosyspki -o pki_check_so

NOTES
-----
This program should display a series of tests ending with "ALL DONE". 
No test files are required to be present. 
It will create a new temporary test directory and offer to delete this on completion.

This is not meant to be representative of good security coding.  The RSA keys
are deliberately made smaller than you should use in practice to save time in
testing. Do not use 512-bit keys in practice.

There is minimal error checking here - we use assert as a blunt instrument - and
we make little or no effort to clean up passwords etc afterwards.

In particular, we save and display unencrypted private keys here to demonstrate 
certain debugging techniques. This should *never* be done in a real production
envrionment.  ALWAYS use the encrypted form and keep the private key string and 
password secret, then wipe them securely immediately after use. (It is not
advisable to prompt the user with the actual password, either.)

*/

#if _MSC_VER >= 1100
	/* Detect memory leaks in MSVC++ */ 
	#define _CRTDBG_MAP_ALLOC
	#include <stdlib.h>
	#include <crtdbg.h>
#else
	#include <stdlib.h>
#endif

#include "diCrPKI.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#ifdef NDEBUG
/* Make sure assertion testing is turned on */
	#undef NDEBUG
#endif
#include <assert.h>

/* In-line instructions to link to the library. 
 * This works for us in MSVC and Borland C
 * -- you may need to do otherwise.
 */
#if _MSC_VER >= 1400 
/* This should work in MS VS2005 and above */
#if _WIN64
	#if _DEBUG
	#define MYLIBDIR "../x64/Debug/"
	#else
	#define MYLIBDIR "../x64/Release/"
	#endif
#else
	#if _DEBUG
	#define MYLIBDIR "../Debug/"
	#else
	#define MYLIBDIR "../Release/"
	#endif
#endif
#pragma comment(lib, MYLIBDIR "diCrPKI.lib")
#elif defined(_WIN32) || defined(WIN32) || defined(__BORLANDC__)
/* Link to library in same dir as this source.
 * This works in old MSVC++ and Borland.
 */
#pragma comment(lib, ".\\diCrPKI.lib")
#endif
#if defined(__BORLANDC__)
#pragma warn -8004     /* "'foo' is assigned a value that is never used" */
#endif


/* SYSTEM-SPECIFIC DIR FNS:
 * Used in `make_new_test_dir` and `remove_test_dir` only.
 */
#ifdef unix
	#define DELCMD "rm"
#else
	#define DELCMD "DEL /Q"
#endif
#ifdef _MSC_VER
	/* MSVC functions */
	#include <direct.h>
	#define MKDIR(d) _mkdir(d)
	#define CHDIR(d) _chdir(d)
	#define RMDIR(d) _rmdir(d)
	#define GETCWD(od, n) _getcwd(od, n)
#elif __BORLANDC__
	/* Borland functions */
	#include <dir.h>
	#define MKDIR(d) mkdir(d)
	#define CHDIR(d) chdir(d)
	#define RMDIR(d) rmdir(d)
	#define GETCWD(od, n) getcwd(od, n)
#elif unix
	/* Linux functions */
	#include <unistd.h>
	#include <sys/stat.h>
	#include <sys/types.h>
	#define MKDIR(d) mkdir(d, 0777)
	#define CHDIR(d) chdir(d)
	#define RMDIR(d) rmdir(d)
	#define GETCWD(od, n) getcwd(od, n)
#else
	/* Take a punt the compiler has inbuilt fns or die!
	   -- replace with your own here if necessary */
	#define MKDIR(d) mkdir(d)
	#define CHDIR(d) chdir(d)
	#define RMDIR(d) rmdir(d)
	#define GETCWD(od, n) getcwd(od, n)
#endif

/* Cope with case-insensitive string comparisons */
#if defined(_MSC_VER)
#include <string.h>
#define stricmp  _stricmp
#define strnicmp _strnicmp
#elif defined(unix) || defined (linux) || defined(__linux) || defined(__APPLE__)
#include <strings.h>
#define stricmp  strcasecmp
#define strnicmp strncasecmp
#else
/* We just hope the function is available natively... */
#endif 

/* Global variables */
char testdir[FILENAME_MAX];
char old_cwd[FILENAME_MAX];
char new_cwd[FILENAME_MAX];

int no_prompt = 0;

/*********************************/
/* SETUP AND UTILITIES FOR TESTS */
/*********************************/

void check_user_options(int argc, char *argv[])
{
	/* Deal with command-line options:
	[no-prompt]
	(we use this when debugging)
	*/
	while (argc > 1)
	{
		if (strcmp(argv[1], "no-prompt") == 0)
			no_prompt = 1;
		else
			fprintf(stderr, "Invalid option '%s'\n", argv[1]);

		argc--;
		argv++;
	}
}

int make_new_test_dir(void)
{
	/* 
	(1) Try and create a sub-dir in current working dir
	(2) If that fails, use _tempnam
	*/

	unsigned char rbytes[4];
	char hex[9];

	/* Use PKI to generate a random address */
	RNG_Bytes(rbytes, sizeof(rbytes), NULL, 0);
	HASH_HexFromBytes(hex, sizeof(hex)-1, rbytes, sizeof(rbytes), 0);

	/* Create a test directory */
	sprintf(testdir, "pkitest.%s", hex);
	printf("Trying to create test directory '%s'...\n", testdir);
	
	/* Use system-specific fns to create and set as default dir */
	if (MKDIR(testdir) != 0)
#ifdef _MSC_VER
	{	/* Check added in version 2.2 in case we run this where we don't have permission */
		char *tempname;
		printf("Unable to create test directory '%s'. Trying with _tempnam...\n", testdir);
		/* Now try using _tempnam (making a copy) */
		tempname = _tempnam("\\", "pki");
		strncpy(testdir, tempname, FILENAME_MAX-1);
		free(tempname);	/* _tempnam uses malloc */
		if (MKDIR(testdir) != 0)
		{
			fprintf(stderr, "ERROR: unable to create a temp directory.");
			exit(EXIT_FAILURE);
		}
	}
#else	/* If not MSVC, we have just failed */
	{
		fprintf(stderr, "ERROR: unable to create a temp directory.");
		exit(EXIT_FAILURE);
	}
#endif

	printf("Created test directory '%s' OK.\n", testdir);

	/* Remember current working directory */
	GETCWD(old_cwd, FILENAME_MAX-1);

	/* Change CWD to our new temp dir */
	CHDIR(testdir);

	/* And check */
	GETCWD(new_cwd, FILENAME_MAX-1);
	printf("Current dir is '%s'\n", new_cwd);

	return 0;
}

void remove_test_dir(char *dirname, char *old_cwd)
{
	/* Use system commands to do the business 
	   --see DELCMD macro above (NB strings concatenate) */
	/* CAUTION: Use this carefully */
	int res;

	if (0 == strlen(dirname))
		return;

	CHDIR(dirname);
	system(DELCMD" *.txt");
	system(DELCMD" *.cer");
	system(DELCMD" *.bin");
	system(DELCMD" *.pfx");
	system(DELCMD" *.p10");
	system(DELCMD" *.p8*");
	system(DELCMD" *.pub");
	system(DELCMD" *.p7*");
	system(DELCMD" *.xml");
	system(DELCMD" *.dat");
	system(DELCMD" *.crl");
	system(DELCMD" *.tmp");
	/* Go back to the CWD we stored at the start */
	if (!old_cwd)
		CHDIR("..");
	else
		CHDIR(old_cwd);
	res = RMDIR(dirname);
	if (res == 0)
		printf("Removed test directory OK.\n");
	else
		printf("ERROR: (%d) failed to remove test directory.\n", res);
}

void create_test_files(void)
/* Create the test files we expect to find */
{
	FILE *fp;

	/* 1. SafeToDelete.txt */
	fp = fopen("!ThisFolderIsSafeToDelete.txt", "wb");
	assert(fp != NULL);
	fprintf(fp, "This folder has been created by the CryptoSys PKI test program.\nIt can be safely deleted at any time.");
	fclose(fp);

	/* 2. Excontent.txt */
	fp = fopen("excontent.txt", "wb");
	assert(fp != NULL);
	fprintf(fp, "This is some sample content.");
	fclose(fp);

	/* 3. abc.txt */
	fp = fopen("abc.txt", "wb");
	assert(fp != NULL);
	fprintf(fp, "abc");
	fclose(fp);

	/* 4. Chinese name 你好.txt */
#ifdef _MSC_VER
	fp = _wfopen(L"你好.txt", L"wb");
#else	// Assume Linux and can cope with Chinese chars
	fp = fopen("你好.txt", "wb");
#endif
	fprintf(fp, "hello world");
	fclose(fp);
}

char *lookup_error(int errcode)
/* Looks up description of error msg and returns
   ptr to static string
*/
{
	static char errmsg[128];

	errmsg[0] = '\0';
	PKI_ErrorLookup(errmsg, sizeof(errmsg), errcode);

	return errmsg;
}

static void disp_error(long errcode)
{
	char szErrMsg[1024];

	PKI_FormatErrorMessage(szErrMsg, sizeof(szErrMsg) - 1, errcode, "");
	printf("%s\n", szErrMsg);
}

static void disp_error_OLD(long result)
{
	char szErrMsg[512];
	long errcode;

	PKI_LastError(szErrMsg, sizeof(szErrMsg) - 1);
	errcode = PKI_ErrorCode();
	printf("ERROR Returned=%ld/ErrorCode=%ld: %s; %s\n", result, PKI_ErrorCode(),
		lookup_error(errcode), szErrMsg);
}

static void pr_bytes(unsigned char *bytes, long nbytes)
{
	long i;

	for (i = 0; i < nbytes; i++)
	{
		if (i && (i % 32) == 0)
			printf("\n");
		else if (i && (i % 4) == 0)
			printf(" ");
		printf("%02x", *bytes++);
	}
	printf("\n");
}

#define pr_hexencode pr_byteshex

static void pr_byteshex(const char *pre, const void *bytes, size_t nbytes, const char *post)
{
	size_t i;
	const int b_perword = 4;
	const int b_perline = 32;
	const unsigned char *pb = (const unsigned char *)bytes;
	if (pre) printf("%s", pre);
	for (i = 0; i < nbytes; i++)
	{
		if (i && (i % b_perline) == 0)
			printf("\n");
		else if (i && (i % b_perword) == 0)
			printf(" ");
		printf("%02x", *pb++);
	}
	if (post) printf("%s", post);
}

static void pr_byteshex16(const char *prefix, const unsigned char *b, size_t n, const char *suffix)
{
	size_t i;
	const size_t nline = 16;

	if (prefix) printf("%s", prefix);
	for (i = 0; i < n; i++)
	{
		if (i && (i % nline) == 0) printf("\n");
		printf("%02X", b[i]);
	}
	if (suffix) printf("%s", suffix);
}

static void pr_byteshexwrap(size_t linelen, const char *prefix, const unsigned char *b, size_t n, const char *suffix)
{
	size_t i;

	if (prefix) printf("%s", prefix);
	for (i = 0; i < n; i++)
	{
		if (i && (i % linelen) == 0) printf("\n");
		printf("%02X", b[i]);
	}
	if (suffix) printf("%s", suffix);
}

static void pr_wrapstr(const char *prefix, const char *s, size_t linelen)
{
	size_t i;
	const char *cp = s;
	size_t nchars = strlen(s);

	if (prefix) printf("%s", prefix);
	for (i = 0; i < nchars; i++) {
		if (i && (i % linelen) == 0) printf("\n");
		printf("%c", s[i]);
	}
	printf("\n");
}

/* Print the contents of a text file */
static void pr_textfile(const char *fname)
{
	FILE *fp;
	int c;
	fp = fopen(fname, "rb");
	if (!fp) return;
	while ((c = fgetc(fp)) != EOF)
	{
		printf("%c", c);
	}
	fclose(fp);
}

/* Dump details of an X.509 file to stdout */
static void x509DumpFile(const char *certname, long nOptions)
{
	char *buf = NULL;
	long nchars;

	nchars = X509_TextDumpToString(NULL, 0, certname, nOptions);
	if (nchars < 0) {
		disp_error(nchars);
		return;
	}
	buf = malloc(nchars + 1);
	nchars = X509_TextDumpToString(buf, nchars, certname, nOptions);
	printf("%s\n", buf);
	free(buf);
}

static int cmp_files(char *file1, char *file2)
/* Compares two binary files: returns 0 if identical 
   or 1 if not identical or -1 if file error */
{
	FILE *fp1, *fp2;
	int c1, c2;
	long len1, len2;
	int result = 0;	/* Innocent until proven guilty */

	fp1 = fopen(file1, "rb");
	if (fp1 == NULL) 
		return -1;
	fp2 = fopen(file2, "rb");
	if (fp2 == NULL)
	{
		fclose(fp1);
		return -1;
	}

	/* Check lengths are equal */
	if (fseek(fp1, 0, SEEK_END) || fseek(fp2, 0, SEEK_END))
	{
		fclose(fp1);
		fclose(fp2);
		return -1;
	}
	len1 = ftell(fp1);
	len2 = ftell(fp2);
	if (len1 < 0 || len2 < 0) result = -1;
	if (len1 != len2) result = 1;
	if (result != 0)
	{
		fclose(fp1);
		fclose(fp2);
		return result;
	}
	rewind(fp1);
	rewind(fp2);

	while ((c1 = fgetc(fp1)) != EOF)
	{
		c2 = fgetc(fp2);
		if (c2 == EOF)
		{	/* File 2 is shorter than file 1 */
			result = 1;
			break;
		}
		if (c1 != c2)
		{	/* Found a mis-match */
			result = 1;
			break;
		}
	}

	if (feof(fp1))
	{	/* Make sure file2 is same length */
		if ((c2 = fgetc(fp2)) != EOF)
			result = 1;
	}

	fclose(fp1);
	fclose(fp2);

	return result;
}

/** Return zero if byte array contains exactly the bytes in hex_ok; else -1 */
int cmp_bytes_with_hex(const unsigned char *bytes, size_t nbytes, const char *hex_ok)
{
	const char *cp;
	size_t i, n;
	int x;
	char hex[3];

	// Check lengths
	n = strlen(hex_ok) / 2;
	if (n != nbytes) return -1;
	// Convert each pair of hex chars to a byte value then compare to value in byte array
	for (cp = hex_ok, i = 0; i < n; i++)
	{
		hex[0] = *cp++;
		hex[1] = *cp++;
		hex[2] = 0;
		x = strtoul(hex, NULL, 16);
		if (x != bytes[i])
			return -1;
	}

	return 0;
}

/* Get length of file or -1 on error. Max 2GB. */
long file_length(const char *fname)
{
	FILE *fp;
	long flen;

	fp = fopen(fname, "rb");
	if (!fp) return -1;
	if (fseek(fp, 0, SEEK_END)) return -1;
	flen = ftell(fp);
	fclose(fp);

	return flen;
}


/****************/
/* TESTS PROPER */
/****************/

void show_version(void)
{
	long result;
	char timestamp[256];
	char platform[16];
	char dllname[FILENAME_MAX];
	char info[256];
	
	printf("\nVersion information, etc...\n");
	result = PKI_Version(0, 0);
	assert(result > 0);
	printf("PKI_Version=%ld\n", result);
	
	result = PKI_LicenceType(0);
	assert(result > 0);
	printf("PKI_LicenceType: %c\n", (char)result);

	result = PKI_ModuleName(dllname, sizeof(dllname)-1, 0);
	assert(result > 0);
	printf("Core DLL = [%s]\n", dllname);

	result = PKI_CompileTime(timestamp, sizeof(timestamp)-1);
	assert(result > 0);
	printf("Toolkit last compiled [%s]\n", timestamp);

	result = PKI_LicenceType(PKI_GEN_PLATFORM);
	printf("IsWin64=%ld\n", result);

	result = PKI_Platform(platform, sizeof(platform)-1);
	assert(result > 0);
	printf("Platform='%s'\n", platform);

	result = PKI_ModuleInfo(info, sizeof(info) - 1, 0);
	assert(result > 0);
	printf("ModuleInfo='%s'\n", info);
}

void init_rng(void)
{
/* If the seed file does not exist, it will be created (changed in [v3.9]) */
	long result;
	const char *fname = "seed.dat";

	printf("\nInitialize the random number generator from a seed file...\n");

	result = RNG_Initialize(fname, 0);
	printf("RNG_Initialize('%s') returns %ld (expected 0)\n", fname, result);
	assert(result == 0);
}

void carol_creates_keys(void)
/* Carol creates a 1024-bit RSA key pair with
   the private key encrypted with password 'password'
*/
{
	char *pubfile = "carol.pub";
	char *epkfile = "carol.p8e";
	char *szPassword = "password";
	long result;
	time_t start, finish;
	double duration;

	printf("\nCarol creates a 1024-bit RSA key pair...\n");

	start = clock();
	result = RSA_MakeKeys(pubfile, epkfile, 1024, PKI_RSAEXP_EQ_65537, 50, 
		1000, szPassword, NULL, 0, PKI_KEYGEN_INDICATE);
	finish = clock();
	assert(result == 0);

	duration = (double)(finish - start) / CLOCKS_PER_SEC;
	printf("Time=%.3lf secs\n", duration);
	printf("...created key files '%s' and '%s'\n", pubfile, epkfile);
}

void carol_creates_ca_cert(void)
{
	char *certfile = "carol.cer";
	char *epkfile = "carol.p8e";
	char *password = "password";
	char *distname = "C=AU;O=Example Corp;CN=Carol";
	char *extns = "subjectKeyIdentifier=deadbeef;rfc822Name=carol@example.com";
	long result;

	printf("\nCarol creates a self-signed CA certificate...\n");

	/* Make an self-signed cert No. 1 valid for 10 years */
	result = X509_MakeCertSelf(certfile, epkfile, 
		1, 10, distname, extns, 0, password, PKI_X509_CA_TRUE);
	assert(result == 0);
	printf("Created self-signed X.509 certificate '%s'\n", certfile);

	/* Use X509_TextDump to dump details to a file then display them */
	x509DumpFile(certfile, 0);
}

void ann_and_ben_create_keys(void)
/* Ann and Ben both create 512-bit key pairs 
(NB we use 512 for speed in this demo)
both using 'password' as the password
(but they don't tell each other or Carol this fact!)
Ben's private key is secured using "pkcs5PBES2"/"des-EDE3-CBC" 
instead of the default "pbeWithSHAAnd3-KeyTripleDES-CBC".
*/
{
	char *annpubfile = "ann.pub";
	char *annepkfile = "ann.p8e";
	char *benpubfile = "ben.pub";
	char *benepkfile = "ben.p8e";
	char *szPassword = "password";
	long keybits = 512;
	long result;
	time_t start, finish;
	double duration;

	printf("\nCreating 512-bit key pair for Ann...\n");
	start = clock();
	result = RSA_MakeKeys(annpubfile, annepkfile, keybits, PKI_RSAEXP_EQ_5, 50, 
		1000, szPassword, NULL, 0, 0);
	finish = clock();
	assert(result == 0);
	duration = (double)(finish - start) / CLOCKS_PER_SEC;
	printf("Time=%.3lf secs\n", duration);
	printf("...created key files '%s' and '%s'\n", annpubfile, annepkfile);

	printf("\nCreating 512-bit key pair for Ben...\n");
	start = clock();
	result = RSA_MakeKeys(benpubfile, benepkfile, keybits, PKI_RSAEXP_EQ_3, 50, 
		1000, szPassword, NULL, 0, PKI_PBES2_3DES);
	finish = clock();
	assert(result == 0);
	duration = (double)(finish - start) / CLOCKS_PER_SEC;
	printf("Time=%.3lf secs\n", duration);
	printf("...created key files '%s' and '%s'\n", benpubfile, benepkfile);
}

void carol_makes_certs_for_ann_and_ben(void)
{
	long result;
	char *acertfile = "ann.cer";
	char *bcertfile = "ben.cer";
	char *issuercert = "carol.cer";
	char *epkfile = "carol.p8e";
	char *szPassword = "password";

	printf("\nCarol makes X.509 certificates for Ann and Ben...\n");

	/* Make two end-user certs valid for 2 years, signed by Carol */
	result = X509_MakeCert(acertfile, issuercert, "ann.pub", epkfile, 
		0x101, 2, "C=AU;O=Example Corp;CN=Ann", NULL, 0, szPassword, 0);
	assert(result == 0);
	printf("Created end-user X.509 certificate '%s'\n", acertfile);

	result = X509_MakeCert(bcertfile, issuercert, "ben.pub", epkfile, 
		0x102, 2, "C=AU;O=Example Corp;CN=Ben", "ben@example.com", 0, szPassword, 
		PKI_SIG_MD5RSA);
	assert(result == 0);
	printf("Created end-user X.509 certificate '%s'\n", bcertfile);
}

void ben_tests_his_keys_in_the_raw(void)
/* Ben uses the "raw" RSA functions to encrypt and sign some random data */
{
	long result;
	char *pubfile = "ben.pub";
	char *epkfile = "ben.p8e";
	char *szPassword = "password";
	long datalen;
	unsigned char *data, *copy;
	char *pubkeystr, *prikeystr;
	long klen;

	printf("\nBen tests his keys using the 'raw' RSA functions...\n");

	/* Read in the keys to strings */
	klen = RSA_ReadAnyPublicKey(NULL, 0, pubfile, 0);
	assert(klen > 0);
	pubkeystr = malloc(klen+1);	/* NB one extra */
	assert(pubkeystr != NULL);
	result = RSA_ReadAnyPublicKey(pubkeystr, klen, pubfile, 0);
	assert(result == klen);
	klen = RSA_ReadAnyPrivateKey(NULL, 0, epkfile, szPassword, 0);
	assert(klen > 0);
	prikeystr = malloc(klen+1);	/* NB one extra */
	assert(prikeystr != NULL);
	result = RSA_ReadAnyPrivateKey(prikeystr, klen, epkfile, szPassword, 0);
	assert(result == klen);

	/* Generate some random data, m, to test it with */
	datalen = RSA_KeyBytes(pubkeystr);
	data = malloc(datalen);
	assert(data != NULL);
	result = RNG_Bytes(data, datalen, "", 0);
	assert (result == 0);
	/* But make sure m < n */
	data[0] = 0x0;
	printf("Random data (%ld bytes)=\n", datalen);
	pr_bytes(data, datalen);
	/* Keep a copy for testing */
	copy = malloc(datalen);
	assert(copy != NULL);
	memcpy(copy, data, datalen);

	/* Encrypt with the public key */
	result = RSA_RawPublic(data, datalen, pubkeystr, 0);
	assert (result == 0);
	printf("Encrypted data=\n");
	pr_bytes(data, datalen);
	/* Decrypt with the private key */
	result = RSA_RawPrivate(data, datalen, prikeystr, 0);
	/* check */
	if (memcmp(copy, data, datalen) == 0)
		printf("Encrypt/decrypt worked OK\n");
	else
		printf("Encrypt/decrypt FAILED!\n");
	
	/* Sign: Encrypt with the private key */
	result = RSA_RawPrivate(data, datalen, prikeystr, 0);
	assert (result == 0);
	printf("Signed data=\n");
	pr_bytes(data, datalen);
	/* Verify: Decrypt with the public key */
	result = RSA_RawPublic(data, datalen, pubkeystr, 0);
	/* check */
	if (memcmp(copy, data, datalen) == 0)
		printf("Sign/verify worked OK\n");
	else
		printf("Sign/verify FAILED!\n");

	/* Make sure we wipe the private key string */
	WIPE_Data(prikeystr, (long)strlen(prikeystr));

	free(copy);
	free(data);
	free(pubkeystr);
	free(prikeystr);
}

void ann_sends_enc_msg_to_ben(void)
{
	long result;
	char *cmsfile = "cmsforben.p7m";
	char *datafile = "excontent.txt";
	char *bcertfile = "ben.cer";
	
	printf("\nAnn sends an encrypted message to Ben...\n");

	result = CMS_MakeEnvData(cmsfile, datafile, bcertfile, NULL, 0, 0);
	/* NB expecting # recipients == 1 to be returned */
	assert(result == 1);
	printf("Created CMS enveloped-data file '%s'\n", cmsfile);
}

void ben_decrypts_msg_from_ann(void)
{
	long result;
	char *cmsfile = "cmsforben.p7m";
	char *outfile = "textfromann.txt";
	char *chkfile = "excontent.txt";
	char *epkfile = "ben.p8e";
	char *szPassword = "password";
	char *prikeystr = NULL;
	long klen;
	
	printf("\nBen decrypts the message received from Ann...\n");

	/* Get Ben's private key */
	klen = RSA_ReadAnyPrivateKey(NULL, 0, epkfile, szPassword, 0);
	assert(klen > 0);
	prikeystr = malloc(klen+1);	/* NB one extra */
	assert(prikeystr != NULL);
	result = RSA_ReadAnyPrivateKey(prikeystr, klen, epkfile, szPassword, 0);
	assert(result == klen);
	
	/* Decrypt the file */
	result = CMS_ReadEnvData(outfile, cmsfile, NULL, prikeystr, 0);
	if (!(result == 0)) disp_error(result);
	assert(result == 0);
	printf("Decrypted CMS enveloped-data to output file '%s'\n", outfile);

	free(prikeystr);

	/* Check we got what we started with */
	if (cmp_files(outfile, chkfile) == 0)
		printf("Decryption OK - files match.\n");
	else
		printf("Decryption failed - not what we started with!\n");
}


char *get_rsa_private_key(char *keyfile, char *password, long *pklen)
/* One-step wrapper function to get private key string from EPK file */
/* NB allocates memory - must be freed later by caller */
{
	long klen;
	char *key;

	klen = RSA_ReadAnyPrivateKey(NULL, 0, keyfile, password, 0);
	if (klen <= 0) return NULL;
	key = malloc(klen+1);	/* NB one extra */
	assert(key != NULL);
	klen = RSA_ReadAnyPrivateKey(key, klen, keyfile, password, 0);
	
	/* If provided, set key length */
	if (pklen) *pklen = klen;
	return key;
}

void ann_sends_encmsg_ben_reads(void)
{
	long result;
	char *cmsfile = "forcarolandben.p7m";
	char *bcertfile = "ben.cer";
	char *certlist = "carol.cer;ben.cer";
	char *msgtosend = "Be in bar at 7 pm wearing a red rose";
	char *prikeystr = NULL;
	char *msgrecvd;
	long mlen;
	
	printf("\nAnn sends an encrypted message and Ben reads it...\n");

	/* Ann creates a CMS message file, this time from a string 
	   that could be read by either Carol or Ben (see certlist) */
	result = CMS_MakeEnvDataFromString(cmsfile, msgtosend, certlist, NULL, 0, 0);
	/* NB expecting # recipients == 2 to be returned */
	assert(result == 2);
	printf("Created CMS enveloped-data file '%s'\n", cmsfile);

	/* Ben reads it directly into a string, using his private key */
	prikeystr = get_rsa_private_key("ben.p8e", "password", NULL);
	assert(prikeystr != NULL);
	/* How long is it? */
	mlen = CMS_ReadEnvDataToString(NULL, 0, cmsfile, bcertfile, prikeystr, 0);
	printf("CMS_ReadEnvDataIntoString returns %ld\n", mlen);
	assert(mlen > 0);
	msgrecvd = malloc(mlen+1);	/* NB one extra */
	mlen = CMS_ReadEnvDataToString(msgrecvd, mlen, cmsfile, bcertfile, prikeystr, 0);
	assert(mlen > 0);
	printf("Message is '%s'\n", msgrecvd);
	/* Check they are the same */
	result = strcmp(msgrecvd, msgtosend);
	if (result == 0)
		printf("SUCCESS: messages match.\n");
	else
		printf("ERROR: messages do not match!\n");
	assert(result == 0);

	/* clean up */
	free(prikeystr);
	free(msgrecvd);

}

void ann_sends_encr_base64_ben_reads(void)
{
	long result;
	char *cmsfile = "forbenp7m.txt";
	char *bcertfile = "ben.cer";
	char *msgtosend = "Be in bar at 7 pm wearing a red rose";
	char *prikeystr = NULL;
	char *msgrecvd;
	long mlen;
	
	printf("\nAnn sends an encrypted message in base64 format...\n");

	/* Ann creates a CMS message file in base64 format */
	result = CMS_MakeEnvDataFromString(cmsfile, msgtosend, bcertfile, NULL, 0, PKI_CMS_FORMAT_BASE64);
	/* NB expecting # recipients == 1 to be returned */
	assert(result == 1);
	printf("Created CMS enveloped-data file '%s'\n", cmsfile);

	/* Ben reads it directly into a string */
	prikeystr = get_rsa_private_key("ben.p8e", "password", NULL);
	assert(prikeystr != NULL);
	/* How long is it? */
	mlen = CMS_ReadEnvDataToString(NULL, 0, cmsfile, bcertfile, prikeystr, PKI_CMS_FORMAT_BASE64);
	printf("CMS_ReadEnvDataIntoString returns %ld\n", mlen);
	if (mlen <= 0) disp_error(mlen);
	assert(mlen > 0);
	msgrecvd = malloc(mlen+1);	/* NB one extra */
	mlen = CMS_ReadEnvDataToString(msgrecvd, mlen, cmsfile, bcertfile, prikeystr, PKI_CMS_FORMAT_BASE64);
	if (mlen <= 0) disp_error(mlen);
	assert(mlen > 0);
	printf("Message is '%s'\n", msgrecvd);
	/* Check they are the same */
	result = strcmp(msgrecvd, msgtosend);
	if (result == 0)
		printf("SUCCESS: messages match.\n");
	else
		printf("ERROR: messages do not match!\n");
	assert(result == 0);

	/* clean up */
	free(prikeystr);
	free(msgrecvd);

}

void ann_signs_msg(void)
{
	long result;
	char *sigfile = "signedbyann.p7m";
	char *infile = "excontent.txt";
	char *epkfile = "ann.p8e";
	char *certfile = "ann.cer";
	char *szPassword = "password";
	char *prikeystr = NULL;
	long klen;

	printf("\nAnn signs a message...\n");

	prikeystr = get_rsa_private_key(epkfile, szPassword, &klen);
	assert(prikeystr != NULL);
	result = CMS_MakeSigData(sigfile, infile, certfile, prikeystr, 0);
	if (result) disp_error(result);
	assert(result == 0);
	printf("Created CMS signed-data file '%s'\n", sigfile);

	free(prikeystr);
}

void ben_verifies_msg_from_ann(void)
/* Actually could be verified by anybody who has Ann's cert */
{
	char *sigfile = "signedbyann.p7m";
	char *data = NULL;
	long datalen;
	char hexdigest[41];
	char hashcontent[41];
	long hashlen;
	long hashalg;

	printf("\nBen verifies the signed message received from Ann...\n");

	/* 1. Get the content data out of the signed-data file */
	datalen = CMS_ReadSigDataToString(NULL, 0, sigfile, 0);
	printf("CMS_ReadSigDataToString returns %ld\n", datalen);
	assert(datalen > 0);
	data = malloc(datalen+1);
	assert(data != NULL);
	datalen = CMS_ReadSigDataToString(data, datalen, sigfile, 0);
	printf("Signed content is '%s'\n", data);

	/* 2. Get the message digest hash from signed-data */
	hashalg = CMS_GetSigDataDigest(hexdigest, sizeof(hexdigest)-1, sigfile, NULL, 0);
	printf("CMS_ReadSigDataToString returns %ld\n", datalen);
	assert(hashalg >= 0);
	printf("Signed digest  = %s\n", hexdigest);

	/* 3. Make an independent hash of content */
	hashlen = HASH_HexFromBytes(hashcontent, sizeof(hashcontent)-1, data, datalen, hashalg);
	printf("Content digest = %s\n", hashcontent);
	
	/* 4. Compare the hash digests */
	if (strncmp(hashcontent, hexdigest, hashlen) == 0)
		printf("SUCCESS: Message digests match\n");
	else
		printf("FAILURE: digests do not match!\n");

	/* Clean up */
	free(data);
}

void ben_reads_signed_data(void)
/* Ben reads the signed-data from Ann, saving as a file */
{
	char *sigfile = "signedbyann.p7m";
	char *outfile = "signedfromann.txt";
	char *origfile = "excontent.txt";
	long result;

	printf("\nBen reads the signed-data received from Ann...\n");

	result = CMS_ReadSigData(outfile, sigfile, 0);
	printf("CMS_ReadSigData returns %ld\n", result);

	/* Compare this file with original */
	result = cmp_files(outfile, origfile);
	if (result == 0)
		printf("SUCCESS: signed data matches original file\n");
	else
		printf("FAILURE: signed data does NOT match original file\n");
	assert(result == 0);
}
	

void ann_signs_base64_ben_verifies(void)
/* Ann creates a signed-data object in base64 format; Ben verifies it. */
{
	long result;
	char *szContent = "This is some sample content.";
	char *sigfile = "signedbyannp7m.txt";
	char *epkfile = "ann.p8e";
	char *certfile = "ann.cer";
	char *szPassword = "password";
	char *prikeystr = NULL;
	long klen;
	char *data = NULL;
	long datalen;
	char hexdigest[41];
	char hashcontent[41];
	long hashlen;
	long hashalg;

	printf("\nAnn creates a signed-data object in base64 format and Ben verifies it...\n");

	prikeystr = get_rsa_private_key(epkfile, szPassword, &klen);
	assert(prikeystr != NULL);
	result = CMS_MakeSigDataFromString(sigfile, szContent, certfile, prikeystr, PKI_CMS_FORMAT_BASE64);
	if (result) disp_error(result);
	assert(result == 0);
	printf("Created base64 CMS signed-data file '%s' from string\n", sigfile);

	free(prikeystr);

	/* 1. Get the content data out of the signed-data file */
	datalen = CMS_ReadSigDataToString(NULL, 0, sigfile, PKI_CMS_FORMAT_BASE64);
	printf("CMS_ReadSigDataToString returns %ld\n", datalen);
	if (datalen <= 0) disp_error(datalen);
	assert(datalen > 0);
	data = malloc(datalen+1);
	assert(data != NULL);
	datalen = CMS_ReadSigDataToString(data, datalen, sigfile, PKI_CMS_FORMAT_BASE64);
	if (datalen <= 0) disp_error(datalen);
	assert(datalen > 0);
	printf("Signed content is '%s'\n", data);

	/* 2. Get the message digest hash from signed-data */
	hashalg = CMS_GetSigDataDigest(hexdigest, sizeof(hexdigest)-1, sigfile, NULL, PKI_CMS_FORMAT_BASE64);
	printf("CMS_GetSigDataDigest returns %ld\n", datalen);
	if (hashalg < 0) disp_error(hashalg);
	assert(hashalg >= 0);
	printf("Signed digest  = %s\n", hexdigest);

	/* 3. Make an independent hash of content */
	hashlen = HASH_HexFromBytes(hashcontent, sizeof(hashcontent)-1, data, datalen, hashalg);
	printf("Content digest = %s\n", hashcontent);
	
	/* 4. Compare the hash digests */
	if (strncmp(hashcontent, hexdigest, hashlen) == 0)
		printf("SUCCESS: Message digests match\n");
	else
		printf("FAILURE: digests do not match!\n");

	/* Alternatively, just verify (new in version 2.8) */
	result = CMS_VerifySigData(sigfile, "", "", PKI_CMS_FORMAT_BASE64);
	printf("CMS_VerifySigData returns %ld (expecting 0 => verified signature and content OK)\n", result);
	if (result != 0) disp_error(result);
	assert(result == 0);

	/* And while we're here, we'll query the signed data file (new v2.8) */
	datalen = CMS_QuerySigData(NULL, 0, sigfile, "signatureAlgorithm", PKI_CMS_FORMAT_BASE64);
	printf("CMS_QuerySigData returns %ld\n", datalen);
	assert(datalen > 0);
	data = realloc(data, datalen+1);
	datalen = CMS_QuerySigData(data, datalen, sigfile, "signatureAlgorithm", PKI_CMS_FORMAT_BASE64);
	printf("signatureAlgorithm='%s'\n", data);

	/* Clean up */
	free(data);
}


#define RAND_DATA_LEN 32

void ben_signs_det_msg(void)
{
	char *detsigfile = "detsigbyben.p7s";
	char *epkfile = "ben.p8e";
	char *certfile = "ben.cer";
	char *datafile = "datafromben.txt";
	char data[RAND_DATA_LEN+1] = { 0 };
	const int datalen = RAND_DATA_LEN;
	char hexdigest[41];
	char *prikey;
	long klen;
	long result;

	int i, cr;
	FILE *fp;

	printf("\nBen signs a detached message...\n");

	/* Ben creates some random text */
	srand((unsigned)time(NULL));
	for (i = 0; i < datalen; i++)
	{
		/* Random printable characters between 32 and 127 */
		cr = (rand() % 95) + ' ';
		data[i] = cr;
	}
	data[i] = 0;
	printf("Ben's random text is '%s'\n", data);

	/* Save to a file */
	fp = fopen(datafile, "w");
	assert(fp != NULL);
	fwrite(data, 1, RAND_DATA_LEN, fp);
	fclose(fp);

	/* Compute hash digest using MD5 */
	HASH_HexFromBytes(hexdigest, sizeof(hexdigest)-1, (unsigned char*)data, datalen, PKI_HASH_MD5);
	printf("MD5(data)=%s\n", hexdigest);

	/* Get private key */
	prikey = get_rsa_private_key(epkfile, "password", &klen);
	assert(prikey != NULL);
	/* Make a detached signature file using MD5 */
	result = CMS_MakeDetachedSig(detsigfile, hexdigest, certfile, prikey, PKI_HASH_MD5);
	if (result != 0) disp_error(result);
	printf("Created detached signature file '%s'\n", detsigfile);
	
	free(prikey);
}

void ann_verifies_det_msg_from_ben(void)
/* Ben must send both the original content and the detached signature to ann */
{
	char *datafile = "datafromben.txt";
	char *detsigfile = "detsigbyben.p7s";
	char *data = NULL;
	long datalen;
	char hexdigest[41];
	char hashcontent[41];
	long hashlen;
	long hashalg;
	FILE *fp;

	printf("\nAnn verifies the detached signature received from Ben...\n");

	/* 1. Get the content data out of the separate file */
	fp = fopen(datafile, "r");
	assert(fp != NULL);
	for (datalen = 0; fgetc(fp) != EOF; datalen++)
		;
	rewind(fp);
	data = malloc(datalen);
	assert(data != NULL);
	fread(data, 1, datalen, fp);
	fclose(fp);

	/* 2. Get the message digest hash from signed-data */
	hashalg = CMS_GetSigDataDigest(hexdigest, sizeof(hexdigest)-1, detsigfile, NULL, 0);
	printf("CMS_ReadSigDataToString returns %ld\n", datalen);
	assert(hashalg >= 0);
	printf("Signed digest  = %s\n", hexdigest);

	/* 3. Make an independent hash of content */
	hashlen = HASH_HexFromBytes(hashcontent, sizeof(hashcontent)-1, data, datalen, hashalg);
	printf("Content digest = %s\n", hashcontent);
	
	/* 4. Compare the hash digests */
	if (strncmp(hashcontent, hexdigest, hashlen) == 0)
		printf("SUCCESS: Message digests match\n");
	else
		printf("FAILURE: digests do not match!\n");

	/* Clean up */
	free(data);

}

void ben_makes_crs(void)
/* Ben prepares a certificate signing request in both PEM and binary formats */
{
	char *reqfile = "ben_crs.txt";
	char *reqfilebin = "ben_crs.bin";
	char *epkfile = "ben.p8e";
	char *password = "password";
	char *dn = "CN=ben;O=Example Corp;OU=Bens Unit;L=Bensville;S=California;C=US";
	long result;

	printf("\nBen prepares a certificate signing request (CRS)...\n");

	result =  X509_CertRequest(reqfile, epkfile, dn, "", password, 0);
	assert(result == 0);
	printf("Created certificate request '%s'\n", reqfile);

	result =  X509_CertRequest(reqfilebin, epkfile, dn, "", password, PKI_X509_FORMAT_BIN);
	assert(result == 0);
	printf("Created binary-format certificate request '%s'\n", reqfilebin);
}

void ann_verifies_her_cert(void)
{
	char *certfile = "ann.cer";
	char *issuercert = "carol.cer";
	char *benscert = "ben.cer";
	char thumb[41];
	char digest[41];
	long diglen;
	unsigned char *data;
	long datalen;
	FILE *fp;
	int c;
	long result;
	char szDate[32];
	time_t ltime;
	struct tm *tmnow;

	printf("\nAnn verifies her certificate...\n");
	
	/* Check dates */
	result = X509_CertIssuedOn(certfile, szDate, sizeof(szDate)-1, 0);
	printf("X509_CertIssuedOn returns %ld: ", result);
	if (result > 0)
		printf("%s\n", szDate);
	else
		printf("FAILURE: Unable to find ValidFrom date in %s.\n", certfile);
	assert(result > 0);

	result = X509_CertExpiresOn(certfile, szDate, sizeof(szDate)-1, 0);
	printf("X509_CertExpiresOn returns %ld: ", result);
	if (result > 0)
		printf("%s\n", szDate);
	else
		printf("FAILURE: Unable to find ValidTo date in %s.\n", certfile);
	assert(result > 0);

	/* Check system clock */
	time(&ltime);
	tmnow = gmtime(&ltime);
	printf("Time now is %04d-%02d-%02dT%02d:%02d:%02dZ\n", 
		tmnow->tm_year+1900, tmnow->tm_mon+1, tmnow->tm_mday,
		tmnow->tm_hour, tmnow->tm_min, tmnow->tm_sec);

	/* Catch cert created in the first minute of the hour (which will not yet be valid) */
	if (0 == tmnow->tm_min)
		printf("Skipping cert validity test as we are in first minute of the hour.\n");
	else
	{
		/* Is Ann's cert valid? */
		result = X509_CertIsValidNow(certfile, 0);
		printf("X509_CertIsValidNow returns %ld: ", result);
		if (result == 0)
			printf("Certificate '%s' is valid now.\n", certfile);
		else
			printf("FAILURE: Certificate '%s' is NOT valid now.\n", certfile);
		assert(result == 0);
	}

	/* Was it issued by Carol? */
	result = X509_VerifyCert(certfile, issuercert, 0);
	printf("X509_VerifyCert returns %ld: ", result);
	if (result == 0)
		printf("Certificate '%s' was issued by '%s'.\n", certfile, issuercert);
	else
		printf("FAILURE: Certificate '%s' was NOT issued by '%s'.\n", certfile, issuercert);
	assert(result == 0);

	/* Was it issued by Ben? */
	result = X509_VerifyCert(certfile, benscert, 0);
	printf("X509_VerifyCert returns %ld: ", result);
	if (result == 0)
		printf("Certificate '%s' was issued by '%s'.\n", certfile, benscert);
	else
		printf("Certificate '%s' was NOT issued by '%s'.\n", certfile, benscert);
	assert(result == PKI_X509_VERIFY_FAILURE);

	/* What is the thumbprint? */
	result = X509_CertThumb(certfile, thumb, sizeof(thumb)-1, 0);
	assert(result > 0);
	printf("Ann's certificate has thumbprint: %s\n", thumb);

	/* Check it the long way... */
	/* (i) Read in the file to memory */
	fp = fopen(certfile, "rb");
	assert(fp != NULL);
	for (datalen = 0; (c = fgetc(fp)) != EOF; datalen++)
		;
	data = malloc(datalen);
	assert(data != NULL);
	rewind(fp);
	fread(data, 1, datalen, fp);
	fclose(fp);
	/* (ii) Make a hash digest of the data */
	diglen = HASH_HexFromBytes(digest, sizeof(digest)-1, data, datalen, PKI_HASH_SHA1);
	assert(diglen > 0);
	printf("Hash digest of file data is     : %s\n", digest);
	free(data);
	/* (iii) Compare */
	result = strncmp(digest, thumb, diglen);
	if (result == 0)
		printf("SUCCESS: digest matches the 'thumbprint'\n");
	else
		printf("FAILURE: digest does not match thumbprint\n");
	assert(result == 0);
}

void ben_resaves_his_public_key(void)
/* How to save the public key in different formats.
   Note we don't need any security for operatons involving the PUBLIC key.
*/
{
	char *pbkfile = "ben.pub";
	char *newpubk = "ben_pubssl.txt";
	char *xmlpubfile = "ben_pub.xml";
	long result;
	char *pubkeystr, *chkkeystr;
	char *xmlstr;
	long klen, nbits, xlen;
	FILE *fp;

	printf("\nBen reads and saves his RSA public key in different formats...\n");

	/* Read and re-save the public key */
	klen = RSA_ReadAnyPublicKey(NULL, 0, pbkfile, 0);
	assert(klen >= 0);
	pubkeystr = malloc(klen+1);	/* NB always one extra */
	assert(pubkeystr != NULL);
	klen = RSA_ReadAnyPublicKey(pubkeystr, klen, pbkfile, 0);
	assert(klen >= 0);
	printf("Ben's public key string (in 'internal' format)=%s\n", pubkeystr);

	/* How long is the key? */
	nbits = RSA_KeyBits(pubkeystr);
	printf("Ben's public key is %ld bits long\n", nbits);

	/* Save again in OpenSSL-compatible format */
	result = RSA_SavePublicKey(newpubk, pubkeystr, PKI_KEY_FORMAT_SSL);
	assert(result == 0);
	printf("Saved as '%s'\n", newpubk);
	/* This is a text file in PEM-style format, so we can look at it */
	pr_textfile(newpubk);

	/* Read in the one we just made */
	klen = RSA_ReadAnyPublicKey(NULL, 0, newpubk, 0);
	assert(klen >= 0);
	chkkeystr = malloc(klen+1);
	assert(chkkeystr != 0);
	klen = RSA_ReadAnyPublicKey(chkkeystr, klen, newpubk, 0);
	assert(klen >= 0);
	/* Check against original */
	/* [v3.0] You can no longer just compare the internal strings as these are
	now encrypted as of version 3.0. Use RSA_KeyHashCode instead.
	*/
	printf("RSA_KeyHashCode(original key)=%08lX\n", RSA_KeyHashCode(pubkeystr));
	printf("RSA_KeyHashCode(check key)   =%08lX\n", RSA_KeyHashCode(chkkeystr));
	result = (RSA_KeyHashCode(chkkeystr) == RSA_KeyHashCode(pubkeystr));
	if (result)
		printf("Private keys match OK\n");
	else
		printf("FAILURE: private keys do NOT match\n");
	assert(result);

	/* Convert public key string to XML format */
	xlen = RSA_ToXMLString(NULL, 0, pubkeystr, 0);
	assert(xlen > 0);
	xmlstr = malloc(xlen+1);	/* NB one extra for a string */
	assert(xmlstr != NULL);
	xlen = RSA_ToXMLString(xmlstr, xlen, pubkeystr, 0);
	assert(xlen > 0);
	printf("Public key in XML format=\n%s\n", xmlstr);
	/* Save as an XML file */
	fp = fopen(xmlpubfile, "w");
	assert(fp != NULL);
	fprintf(fp, "<?xml version=\"1.0\"?>\n");
	fprintf(fp, "%s\n", xmlstr);
	assert(fclose(fp) == 0);
	printf("Created file '%s'\n", xmlpubfile);

	free(pubkeystr);
	free(chkkeystr);
	free(xmlstr);
}

void ben_examines_his_private_key(void)
/* How to save your private key in different formats
   CAUTION: saving the private key in unencrypted "PrivateKeyInfo" format
   or in XML format should NEVER be done with a production key.
*/
{
	char *epkfile = "ben.p8e";
	char *prifile = "ben_pri.txt";
	char *xmlfile = "ben_pri.xml";
	char *certfile = "ben.cer";
	long result;
	char *prikeystr, *pubkeystr, *chkkeystr;
	char *xmlstr;
	long klen, nbits, nbits1, xlen;
	char password[256];
	long pwdlen, i;
	FILE *fp;

	printf("\nBen reads, checks and saves his RSA keys in different formats...\n");

	/* Prompt for private key password */
	printf("Prompt the user for the private-key password...\n");
	printf("Hint: the password is 'password'\n");
	pwdlen = PWD_Prompt(password, sizeof(password)-1, "Hint: Password is 'password'");
	
	/* Fail gently if wrong password entered */
	if (strcmp(password, "password"))
	{
		printf("Woops! That was the wrong password.\n");
		return;
	}

	/* Read encrypted private key into internal format */
	klen = RSA_ReadAnyPrivateKey(NULL, 0, epkfile, password, 0);
	assert(klen >= 0);
	prikeystr = malloc(klen+1);	/* NB always one extra */
	assert(prikeystr != NULL);
	klen = RSA_ReadAnyPrivateKey(prikeystr, klen, epkfile, password, 0);
	assert(klen >= 0);
	/* CAUTION: you should never show this string in practice! */
	printf("Ben's private key string (in 'internal' format)=%s\n", prikeystr);

	/* How long is the key? */
	nbits = RSA_KeyBits(prikeystr);
	printf("Ben's private key is %ld bits long\n", nbits);
	assert(nbits > 0);

	/* Read in the public key from ben's certificate */
	klen = RSA_ReadAnyPublicKey(NULL, 0, certfile, 0);
	assert(klen >= 0);
	pubkeystr = malloc(klen+1);	/* NB always one extra */
	assert(pubkeystr != NULL);
	klen = RSA_ReadAnyPublicKey(pubkeystr, klen, certfile, 0);
	assert(klen >= 0);
	/* This will be different every time you run this */
	printf("Ben's public key string (in 'internal' format)=%s\n", pubkeystr);

	/* How long is the key? */
	nbits1 = RSA_KeyBits(prikeystr);
	printf("Ben's public key is %ld bits long\n", nbits1);
	assert(nbits == nbits1);

	/* Check these two internal key strings match */
	result = RSA_KeyMatch(prikeystr, pubkeystr);
	printf("RSA_KeyMatch returns %ld (expected 0)\n", result);
	assert(result == 0);

	/* Save private key as unencrypted PrivatekeyInfo in text format compatible with OpenSSL */
	/* CAUTION: don't do this with a production key */
	result = RSA_SavePrivateKeyInfo(prifile, prikeystr, PKI_KEY_FORMAT_SSL);
	assert(result == 0);
	printf("Saved as '%s'\n", prifile);
	/* This is a text file in PEM-style format, so we can look at it */
	pr_textfile(prifile);

	/* Read in unencrypted private key */
	klen = RSA_ReadPrivateKeyInfo(NULL, 0, prifile, 0);
	assert(klen >= 0);
	chkkeystr = malloc(klen+1);
	assert(chkkeystr != 0);
	klen = RSA_ReadPrivateKeyInfo(chkkeystr, klen, prifile, 0);
	assert(klen >= 0);
	/* Check against original */
	printf("RSA_KeyHashCode(original key)=%08lX\n", RSA_KeyHashCode(prikeystr));
	printf("RSA_KeyHashCode(check key)   =%08lX\n", RSA_KeyHashCode(chkkeystr));
	result = (RSA_KeyHashCode(chkkeystr) == RSA_KeyHashCode(prikeystr));
	if (result)
		printf("Private keys match OK\n");
	else
		printf("FAILURE: private keys do NOT match\n");
	assert(result);

	/* Save private key in (unencrypted) XML format using non-standard hex format */
	xlen = RSA_ToXMLString(NULL, 0, prikeystr, PKI_XML_HEXBINARY);
	assert(xlen > 0);
	xmlstr = malloc(xlen+1);	/* NB one extra for a string */
	assert(xmlstr != NULL);
	xlen = RSA_ToXMLString(xmlstr, xlen, prikeystr, PKI_XML_HEXBINARY);
	assert(xlen > 0);
	printf("Private key in XML format=\n%s\n", xmlstr);
	/* Save as an XML file */
	fp = fopen(xmlfile, "w");
	assert(fp != NULL);
	fprintf(fp, "<?xml version=\"1.0\"?>\n");
	fprintf(fp, "%s\n", xmlstr);
	assert(fclose(fp) == 0);
	printf("Created file '%s'\n", xmlfile);

	/* Clear the password */
	result = WIPE_Data(password, pwdlen);
	assert(result == 0);
	/* Check all zeroised */
	for (i = 0; i < pwdlen; i++)
		assert(password[i] == 0);

	/* Wipe the unecrypted key file */
	result = WIPE_File(prifile, 0);
	assert(result == 0);
	printf("%s has been securely wiped\n", prifile);

	/* Wipe the private key string */
	result = WIPE_Data(prikeystr, (long)strlen(prikeystr));
	assert(result == 0);

	free(prikeystr);
	free(pubkeystr);
	free(chkkeystr);
	free(xmlstr);
}

void carol_reads_public_key_from_cert(void)
/* Example showing how we can read the public key from both an X.509 certificate
   and from the public key file made using RSA_MakeKeys. 
*/
{
	char *pbkfile  = "carol.pub";
	char *certfile = "carol.cer";
	long result;
	char *pubkeystr, *pubkeystr1;
	long klen, nbits, nbits1;

	printf("\nCarol extracts her public key from her X.509 certificate...\n");

	/* Read in public key from X.509 certificate */
	klen = RSA_ReadAnyPublicKey(NULL, 0, certfile, 0);
	assert(klen >= 0);
	pubkeystr = malloc(klen+1);
	assert(pubkeystr != NULL);
	klen = RSA_ReadAnyPublicKey(pubkeystr, klen, certfile, 0);
	assert(klen >= 0);
	nbits = RSA_KeyBits(pubkeystr);
	printf("Carol's key is %ld bits long\n", nbits);

	/* And from public key file */
	klen = RSA_ReadAnyPublicKey(NULL, 0, pbkfile, 0);
	assert(klen >= 0);
	pubkeystr1 = malloc(klen+1);
	assert(pubkeystr1 != NULL);
	klen = RSA_ReadAnyPublicKey(pubkeystr1, klen, pbkfile, 0);
	assert(klen >= 0);
	nbits1 = RSA_KeyBits(pubkeystr1);
	assert(nbits1 == nbits);

	/* [v3.0] You can no longer just compare the internal strings as these are
	now encrypted as of version 3.0. Use RSA_KeyHashCode instead.
	*/
	printf("RSA_KeyHashCode(original key)=%08lX\n", RSA_KeyHashCode(pubkeystr));
	printf("RSA_KeyHashCode(check key)   =%08lX\n", RSA_KeyHashCode(pubkeystr1));
	result = (RSA_KeyHashCode(pubkeystr) == RSA_KeyHashCode(pubkeystr1));
	if (result)
		printf("Public keys match OK\n");
	else
		printf("FAILURE: public keys do NOT match\n");
	assert(result);

	free(pubkeystr);
	free(pubkeystr1);
}

void ann_makes_cert_chain(void)
/* Ann creates a PKCS#7 certificate chain with her cert and the issuer's (carol's)
   then she extracts the certificates from it */
{
	long result;
	char *certlist;
	char *p7file = "ann.p7c";
	char certname[FILENAME_MAX], distname[256];
	int ncerts, index;

	printf("\nAnn makes a PKCS7 certificate chain...\n");

	/* Create list of certs in chain separated by semicolons */
	certlist = "carol.cer;ann.cer";

	/* Make a certs-only signed data file */
	result = CMS_MakeSigData(p7file, "", certlist, "", PKI_CMS_CERTS_ONLY);
	printf("CMS_MakeSigData returns %ld (expecting 0)\n", result);
	assert(result == 0);
	printf("Created PKCS#7 cert chain file '%s'\n", p7file);

	/* Now extract all the certificates from it */
	index = 0;	/* How many certs? */
	ncerts = X509_GetCertFromP7Chain(NULL, p7file, index, 0);
	printf("X509_GetCertFromP7Chain(0) returns %d (= no of certs)\n", ncerts);
	assert(ncerts > 0);
	for (index = 1; index <= ncerts; index++)
	{
		sprintf(certname, "cert%d.cer", index);
		result = X509_GetCertFromP7Chain(certname, p7file, index, 0);
		printf("X509_GetCertFromP7Chain(%d) returns %ld (= file size)\n", index, result);
		assert(result > 0);
		/* Query the certificate */
		result = X509_CertSubjectName(certname, distname, sizeof(distname)-1, "", 0);
		assert(result > 0);
		printf("%s has subject name '%s'\n", certname, distname);
	}
}

void ben_makes_pfx(void)
/* Ben creates a PKCS#12 PFX file with his encrypted private key and certificate
   then he verifies the signature and extracts his key and certificate from it */
{
	char *pfxfile =  "ben.pfx";
	char *certfile = "ben.cer";
	char *epkfile =  "ben.p8e";
	char *password = "password";
	char *certcopy = "ben_copy.cer";
	char *epkcopy =  "ben_epk_copy.bin";
	long result;
	long nchars;
	char *prikeybuf;
	char *pubkeybuf;
	long ncode1, ncode2, nret;

	printf("\nBen makes a PKCS12 PFX file...\n");

	/* Create the PFX file */
	result = PFX_MakeFile(pfxfile, certfile, epkfile, password, "Ben's Friendly ID", 0);
	printf("PFX_MakeFile returns %ld (expecting 0)\n", result);
	assert(result == 0);
	printf("Created PKCS#12 file '%s'\n", pfxfile);

	/* Verify the signature */
	result = PFX_VerifySig(pfxfile, password, 0);
	printf("PFX_VerifySig returns %ld (expecting 0 => verified OK)\n", result);
	assert(result == 0);

	/* Extract the private key as a file (still encrypted) */
	result = RSA_GetPrivateKeyFromPFX(epkcopy, pfxfile, 0);
	printf("RSA_GetPrivateKeyFromPFX returns %ld (= file size)\n", result);
	assert(result > 0);
	printf("Extracted encrypted private key file '%s'\n", epkcopy);
	/* can we read the key? */
	result = RSA_ReadAnyPrivateKey(NULL, 0, epkcopy, password, 0);
	printf("RSA_ReadAnyPrivateKey returns %ld (= internal key size)\n", result);
	assert(result > 0);

	/* Extract the certificate */
	// NB As of [v3.8] the cert in a PFX is encrypted by default, so we need the password
	result = X509_GetCertFromPFX(certcopy, pfxfile, password, 0);
	printf("X509_GetCertFromPFX returns %ld (= file size)\n", result);
	assert(result > 0);
	printf("Extracted certificate file '%s'\n", certcopy);
	/* compare with original */
	result = cmp_files(certcopy, certfile);
	printf("%s to original file\n", (result == 0 ? "EQUAL" : "NOT EQUAL!!"));
	assert(result == 0);

	/* Read the private key from the PFX file into an internal string [new in v3.8] */
	nchars = RSA_ReadPrivateKeyFromPFX(NULL, 0, pfxfile, password, 0);
	printf("RSA_ReadPrivateKeyFromPFX returns %ld (expecting > 0)\n", nchars);
	assert(nchars > 0);
	prikeybuf = malloc(nchars+1);
	assert(prikeybuf);
	nchars = RSA_ReadPrivateKeyFromPFX(prikeybuf, nchars, pfxfile, password, 0);
	/* display some info about the key */
	printf("Private key length = %ld\n", RSA_KeyBits(prikeybuf));
	ncode1 = RSA_KeyHashCode(prikeybuf);
	printf("KeyHashCode=%lX\n", ncode1);
	nret = RSA_CheckKey(prikeybuf, 0);
	printf("RSA_CheckKey returns %ld: (PKI_VALID_PRIVATEKEY=%d)\n", nret, PKI_VALID_PRIVATEKEY);
	assert(nret == PKI_VALID_PRIVATEKEY);

	/* Convert the internal private key string into a public one */
	nchars = RSA_PublicKeyFromPrivate(NULL, 0, prikeybuf, 0);
	printf("RSA_PublicKeyFromPrivate returns %ld (expecting > 0)\n", nchars);
	assert(nchars > 0);
	pubkeybuf = malloc(nchars+1);
	assert(pubkeybuf);
	nchars = RSA_PublicKeyFromPrivate(pubkeybuf, nchars, prikeybuf, 0);
	/* display some info about the key */
	printf("Public key length = %ld\n", RSA_KeyBits(pubkeybuf));
	ncode2 = RSA_KeyHashCode(pubkeybuf);
	printf("KeyHashCode=%lX\n", ncode2);
	nret = RSA_CheckKey(pubkeybuf, 0);
	printf("RSA_CheckKey returns %ld: (PKI_VALID_PUBLICKEY=%d)\n", nret, PKI_VALID_PUBLICKEY);
	assert(nret == PKI_VALID_PUBLICKEY);
	/* we should have got the same KeyHashCode for both */
	printf("KeyHashCodes are %s\n", (ncode1 == ncode2 ? "EQUAL" : "NOT EQUAL!!"));
	assert(ncode1 == ncode2);

	free(prikeybuf);
	free(pubkeybuf);

}

void ben_encrypts_raw_key(void)
/* Ben has a 192-bit session key (Content Encryption Key, CEK) he wants to send encrypted to ann */
/* INPUT: CEK; CEK length; Ann's public key file.
   OUTPUT: encrypted block.
*/
{
	long result;
	char *pubfile = "ann.pub";
	unsigned char cek[24] = 
	{	/* 0xffeeddccbbaa99887766554433221100fedcba9876543210 */
		0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, 
		0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00, 
		0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10 
	};
	unsigned char *block;
	char *pubkeystr;
	long klen, blocklen, ceklen;
	char *fname = "raw_enc_key_for_ann.bin";
	FILE *fp;

	printf("\nBen uses 'raw' RSA to encrypt a secret for Ann...\n");

	/* Read in Ann's public key to internal string */
	klen = RSA_ReadAnyPublicKey(NULL, 0, pubfile, 0);
	assert(klen > 0);
	pubkeystr = malloc(klen+1);	/* NB one extra */
	assert(pubkeystr != NULL);
	result = RSA_ReadAnyPublicKey(pubkeystr, klen, pubfile, 0);
	assert(result == klen);

	/* Create a block equal in length to the RSA key modulus */
	blocklen = RSA_KeyBytes(pubkeystr);
	assert(blocklen > 0);
	block = malloc(blocklen);
	assert(block != NULL);

	/* Encode the message (i.e. the CEK) for encryption */
	ceklen = sizeof(cek);
	result = RSA_EncodeMsg(block, blocklen, cek, ceklen, PKI_EME_PKCSV1_5);
	assert(result == 0);

	printf("CEK=\n");
	pr_bytes(cek, ceklen);
	printf("Block before encryption=\n");
	pr_bytes(block, blocklen);

	/* Encrypt the block using the RSA public key */
	result = RSA_RawPublic(block, blocklen, pubkeystr, 0);
	assert(result == 0);
	printf("Encrypted block=\n");
	pr_bytes(block, blocklen);

	/* Save as a binary file (and send to Ann) */
	fp = fopen(fname, "wb");
	assert(fp != NULL);
	fwrite(block, 1, blocklen, fp);
	fclose(fp);

	printf("Saved encrypted key as file '%s'\n", fname);

	free(pubkeystr);
	free(block);

}

void ann_decrypts_raw_key(void)
/* Ann decrypts the session key (CEK) from Ben using her RSA private key */
/* INPUT: encrypted block; Ann's private key file; password.
   OUTPUT: CEK.
*/
{
	long result;
	char *epkfile = "ann.p8e";
	char *szPassword = "password";
	unsigned char *block, *cek;
	char *prikeystr;
	long klen, blocklen, ceklen;
	char *fname = "raw_enc_key_for_ann.bin";
	FILE *fp;

	printf("\nAnn decrypts the 'raw' data from Ben...\n");

	/* Read in Ann's private key to an internal string */
	klen = RSA_ReadAnyPrivateKey(NULL, 0, epkfile, szPassword, 0);
	assert(klen > 0);
	prikeystr = malloc(klen+1);	/* NB one extra */
	assert(prikeystr != NULL);
	result = RSA_ReadAnyPrivateKey(prikeystr, klen, epkfile, szPassword, 0);
	assert(result == klen);

	/* Create a block equal in length to the RSA key modulus */
	blocklen = RSA_KeyBytes(prikeystr);
	assert(blocklen > 0);
	block = malloc(blocklen);
	assert(block != NULL);

	/* Read in the encrypted data from the binary file 
	   -- it MUST be the same length as the key */
	fp = fopen(fname, "rb");
	assert(fp != NULL);
	result = (long)fread(block, 1, blocklen, fp);
	fclose(fp);
	assert (result == blocklen);
	printf("Encrypted block=\n");
	pr_bytes(block, blocklen);

	/* Decrypt the block using the RSA private key */
	result = RSA_RawPrivate(block, blocklen, prikeystr, 0);
	assert(result == 0);
	printf("Decrypted block=\n");
	pr_bytes(block, blocklen);

	/* Decode the block to extract the message (the CEK) */
	/* how long is it? */
	ceklen = RSA_DecodeMsg(NULL, 0, block, blocklen, PKI_EME_PKCSV1_5);
	printf("RSA_DecodeMsg returns %ld\n", ceklen);
	assert(ceklen > 0);
	/* allocate memory and extract */
	cek = malloc(ceklen);
	result = RSA_DecodeMsg(cek, ceklen, block, blocklen, PKI_EME_PKCSV1_5);
	assert(result == ceklen);

	printf("CEK=\n");
	pr_bytes(cek, ceklen);

	/* Now use the CEK ... */

	/* Make sure we wipe the private key string and CEK */
	WIPE_Data(prikeystr, (long)strlen(prikeystr));
	WIPE_Data(cek, ceklen);

	free(prikeystr);
	free(block);
	free(cek);

}

void ann_signs_raw_msg(void)
/* Ann uses raw RSA to sign a message (actually its message digest) */
/* INPUT: Message-to-be-signed; hash algorithm to use (SHA-1); Ann's private key file; password.
   OUTPUT: signature value.
*/
{
	long result;
	char *epkfile = "ann.p8e";
	char *szPassword = "password";
	char *msg = "This is some sample content."; 
	unsigned char hash[PKI_SHA1_BYTES];
	unsigned char *block;
	char *prikeystr;
	long klen, blocklen, hashlen;
	char *fname = "raw_sig_by_ann.bin";
	FILE *fp;

	printf("\nAnn uses raw RSA to sign a message...\n");

	/* Ann reads in her private key */
	klen = RSA_ReadAnyPrivateKey(NULL, 0, epkfile, szPassword, 0);
	assert(klen > 0);
	prikeystr = malloc(klen+1);	/* NB one extra */
	assert(prikeystr != NULL);
	result = RSA_ReadAnyPrivateKey(prikeystr, klen, epkfile, szPassword, 0);
	assert(result == klen);

	/* Create a block equal in length to the RSA key modulus */
	blocklen = RSA_KeyBytes(prikeystr);
	assert(blocklen > 0);
	block = malloc(blocklen);
	assert(block != NULL);

	/* Compute the SHA-1 message digest of the message-to-be-signed */
	hashlen = sizeof(hash);
	result = HASH_Bytes(hash, hashlen, (unsigned char *)msg, (long)strlen(msg), PKI_HASH_SHA1);
	printf("SHA-1('%s')=\n", msg);
	pr_bytes(hash, hashlen);

	/* Encode the message digest for signature (EMSA) */
	result = RSA_EncodeMsg(block, blocklen, hash, hashlen, 
		PKI_EMSIG_PKCSV1_5 + PKI_HASH_SHA1 + PKI_EMSIG_DIGESTONLY);
	assert(result == 0);
	printf("Block before signing=\n");
	pr_bytes(block, blocklen);

	/* (NOTE: we could have omitted the digest computation above and encoded the message
	   directly without using the PKI_EMSIG_DIGESTONLY option.) */

	/* Sign the block using the private key */
	result = RSA_RawPrivate(block, blocklen, prikeystr, 0);
	assert(result == 0);
	printf("Signature block=\n");
	pr_bytes(block, blocklen);

	/* Save as a binary file (and send to Ben) */
	fp = fopen(fname, "wb");
	assert(fp != NULL);
	fwrite(block, 1, blocklen, fp);
	fclose(fp);
	printf("Saved signature value as file '%s'\n", fname);

	/* Make sure we wipe the private key string */
	WIPE_Data(prikeystr, (long)strlen(prikeystr));
	free(prikeystr);
	free(block);

}

void ben_verifies_raw_msg(void)
/* Ben verifies the raw RSA signature received from Ann */
/* INPUT: signature value; Ann's public key file; HASH(message-to-be-verified); hash algorithm used (SHA-1).
   OUTPUT: "Valid signature" or "Invalid signature".
*/
{
	long result;
	char *pubfile = "ann.pub";
	unsigned char cfhash[PKI_SHA1_BYTES] = 
	{	/* 406aec085279ba6e16022d9e0629c0229687dd48 */
		0x40, 0x6A, 0xEC, 0x08, 0x52, 0x79, 0xBA, 0x6E, 
		0x16, 0x02, 0x2D, 0x9E, 0x06, 0x29, 0xC0, 0x22, 
		0x96, 0x87, 0xDD, 0x48 };
	unsigned char *block, *hash;
	char *pubkeystr;
	long klen, blocklen, hashlen;
	char *fname = "raw_sig_by_ann.bin";
	FILE *fp;

	printf("\nBen verifies the raw RSA signature received from Ann...\n");

	/* Ann reads in her private key */
	klen = RSA_ReadAnyPublicKey(NULL, 0, pubfile, 0);
	assert(klen > 0);
	pubkeystr = malloc(klen+1);	/* NB one extra */
	assert(pubkeystr != NULL);
	result = RSA_ReadAnyPublicKey(pubkeystr, klen, pubfile, 0);
	assert(result == klen);

	/* Create a block equal in length to the RSA key modulus */
	blocklen = RSA_KeyBytes(pubkeystr);
	assert(blocklen > 0);
	block = malloc(blocklen);
	assert(block != NULL);

	/* Read in the signature value from the binary file 
	   -- it MUST be the same length as the key */
	fp = fopen(fname, "rb");
	assert(fp != NULL);
	result = (long)fread(block, 1, blocklen, fp);
	fclose(fp);
	assert (result == blocklen);
	printf("Signature value=\n");
	pr_bytes(block, blocklen);

	/* Verify the block by decrypting using the RSA public key */
	result = RSA_RawPublic(block, blocklen, pubkeystr, 0);
	assert(result == 0);
	printf("Decrypted block=\n");
	pr_bytes(block, blocklen);

	/* Decode the block to extract the message digest */
	/* how long is it? */
	hashlen = RSA_DecodeMsg(NULL, 0, block, blocklen, PKI_EMSIG_PKCSV1_5);
	printf("RSA_DecodeMsg returns %ld\n", hashlen);
	assert(hashlen > 0);
	/* allocate memory and extract */
	hash = malloc(hashlen);
	result = RSA_DecodeMsg(hash, hashlen, block, blocklen, PKI_EMSIG_PKCSV1_5);
	assert(result == hashlen);

	printf("HASH= ");
	pr_bytes(hash, hashlen);

	/* Ben has already independently computed the message digest,
	   so he compares the two digest values */
	printf("HASH'=");
	pr_bytes(cfhash, sizeof(cfhash));
	result = memcmp(cfhash, hash, hashlen);
	printf("%s\n", (result == 0 ? "Valid signature" : "INVALID SIGNATURE"));
	assert (result == 0);


	/* Clean up */
	free(pubkeystr);
	free(block);
	free(hash);
}

void ben_creates_cjk_ca_cert(void)
/* Ben creates a self-signed certificate with his name in Chinese */
{
	char *certfile = "ben_china.cer";
	char *epkfile = "ben.p8e";
	char *password = "password";
	char *distname;
	long keyusage;
	long result;
	char *query;
	char soutput[64];

	printf("\nBen creates a self-signed certificate with his Chinese name...\n");

	/* Set Key Usage flags */
	keyusage = PKI_X509_KEYUSAGE_DIGITALSIGNATURE 
		+ PKI_X509_KEYUSAGE_KEYCERTSIGN + PKI_X509_KEYUSAGE_CRLSIGN;

	/* Set name using UTF-8-encoded chinese characters:
	CN=ben (U+672C)
	C= zhong guo (U+4E2D, U+570B)
	OU=zong ju (U+7E3D, U+5C40)
	*/
	distname = "CN=#xe69cac;C=#xe4b8ade59c8b;OU=#xe7b8bde5b180";

	/*	Make an self-signed cert No. 0x888 valid for 8 years 
		using sha256WithRSAEncryption for signature */
	result = X509_MakeCertSelf(certfile, epkfile, 
		0x888, 8, distname, "ben@ho.com.cn", keyusage, password, 
		PKI_X509_UTF8|PKI_SIG_SHA256RSA);
	assert(result == 0);
	printf("Created self-signed X.509 certificate '%s'\n", certfile);

	/* Now query the certificate */
	query = "signatureAlgorithm";
	result = X509_QueryCert(soutput, sizeof(soutput)-1, certfile, query, 0);
	printf("%s='%s'\n", query, soutput);
}

void damien_creates_csr(void)
/* Damien creates a certificate signing request (CSR) after making himself 
   a pair of RSA keys */
{
	char *pubfile = "damien_pub.bin";
	char *epkfile = "damien_epk.bin";
	char *csrfile = "damien_csr.p10";
	char *szPassword = "password";
	long keybits = 512;
	long result;
	time_t start, finish;
	double duration;
	char *dn;

	printf("\nCreating 512-bit key pair for Damien...\n");
	start = clock();
	result = RSA_MakeKeys(pubfile, epkfile, keybits, PKI_RSAEXP_EQ_65537, 50, 
		1000, szPassword, NULL, 0, 0);
	finish = clock();
	assert(result == 0);
	duration = (double)(finish - start) / CLOCKS_PER_SEC;
	printf("Time=%.3lf secs\n", duration);
	printf("...created key files '%s' and '%s'\n", pubfile, epkfile);

	printf("\nDamien creates a certificate signing request...\n");
	dn="CN=Damien;O=Beelzebub, Inc.";
	result =  X509_CertRequest(csrfile, epkfile, dn, NULL, szPassword, 0);
	assert(result == 0);
	printf("Created certificate request '%s'\n", csrfile);

	/* Dump the details of the CSR to a text file then display it */
	result = X509_TextDump("dump.txt", csrfile, 0);
	assert(result == 0);
	pr_textfile("dump.txt");

}

void carol_makes_cert_from_damiens_csr(void)
{
	long result;
	char *certfile = "damien.cer";
	char *csrfile = "damien_csr.p10";
	char *issuercert = "carol.cer";
	char *epkfile = "carol.p8e";
	char *szPassword = "password";
	char *extns;
	char *p7file;
	char *certlist;

	printf("\nCarol makes an X.509 certificate for Damien using his Certificate Signing Request...\n");

	/* Carol makes and signs an end-user certificate for Damien valid for 15 years
	   starting on 1 January 2010, adding her KeyIdentifier.
	   Note we pass Damien's CSR filename instead of the public key file 
	   and leave the DN empty (this is important) */
	extns = "notBefore=2010-01-01T12:00";
	result = X509_MakeCert(certfile, issuercert, csrfile, epkfile, 
		0x666, 15, "", extns, 0, szPassword, PKI_X509_AUTHKEYID);
	assert(result == 0);
	printf("Created end-user X.509 certificate '%s'\n", certfile);

	/* Dump the details of the certificate to a text file then display it */
	x509DumpFile(certfile, 0);

	/* Carol now creates a PKCS7 certificate chain for Damien containing her
	   certificate and Damien's new one. This is made in PEM format and so we use
	   the .p7b extension, which MS seems to prefer over .p7c for PEM files */
	p7file = "damien.p7b";
	certlist = "carol.cer;damien.cer";
	result = CMS_MakeSigData(p7file, "", certlist, "", PKI_CMS_CERTS_ONLY | PKI_CMS_FORMAT_BASE64);
	assert(result == 0);
	printf("Created PKCS7 certs-only chain file '%s'\n", p7file);
	pr_textfile(p7file);

}

void damien_reads_new_p7_chain_file(void)
{
	long result;
	char *p7file = "damien.p7b";
	long index, ncerts;
	char certfile[FILENAME_MAX];
	char subjectname[256];
	char serialnumber[64];
	char notbefore[32], notafter[32];

	printf("\nDamien validates the P7 cert chain file from Carol and extracts all the certificates...\n");

	/* Validate the certificate chain file */
	result = X509_ValidatePath(p7file, "", 0);
	printf("X509_ValidatePath returns %ld (expecting 0)\n", result);
	printf("Cert chain '%s' is %s\n", p7file, (result == 0 ? "VALID" : "NOT VALID: ERROR"));
	assert(result == 0);
	
	/* Extract all the certificates from it */
	index = 0;	/* How many certs? */
	ncerts = X509_GetCertFromP7Chain(NULL, p7file, index, 0);
	printf("X509_GetCertFromP7Chain(0) returns %ld (= no of certs)\n", ncerts);
	assert(ncerts > 0);
	for (index = 1; index <= ncerts; index++)
	{
		sprintf(certfile, "cert-%ld.cer", index);
		result = X509_GetCertFromP7Chain(certfile, p7file, index, 0);
		printf("X509_GetCertFromP7Chain(%ld) returns %ld (= file size)\n", index, result);
		assert(result > 0);
		/* Query the certificate */
		printf("%s:\n", certfile);
		result = X509_QueryCert(serialnumber, sizeof(serialnumber)-1, certfile, "serialNumber", 0);
		assert(result > 0);
		printf("  serialNumber: %s\n", serialnumber);
		result = X509_QueryCert(subjectname, sizeof(subjectname)-1, certfile, "subjectName", 0);
		assert(result > 0);
		printf("  subjectName: %s\n", subjectname);
		result = X509_QueryCert(notbefore, sizeof(notbefore)-1, certfile, "notBefore", 0);
		assert(result > 0);
		result = X509_QueryCert(notafter, sizeof(notafter)-1, certfile, "notAfter", 0);
		assert(result > 0);
		printf("  notBefore: %s  notAfter: %s\n", notbefore, notafter);
	}
}

void carol_revokes_damiens_cert(void)
{
	long result;
	char *crlfile = "carol.crl";
	char *issuercert = "carol.cer";
	char *epkfile = "carol.p8e";
	char *password = "password";
	char *extns, *revokedcerts;

	printf("\nCarol revokes Damien's certificate...\n");

	/* Carol is issuing this CRL on 1 April 2010 */
	extns = "thisUpdate=2010-04-01T12:00";
	/* Damien's certificate serial number was 0x666 - it was revoked on 31 March 2010 */
	revokedcerts = "#x666,2010-03-31T12:01:59";

	/* Carol makes and signs a certificate revocation list in PEM format */
	result = X509_MakeCRL(crlfile, issuercert, epkfile, password, revokedcerts, extns, PKI_X509_FORMAT_PEM);
	assert(result == 0);
	printf("Created certificate revocation list (CRL) '%s'\n", crlfile);
	x509DumpFile(crlfile, 0);
	pr_textfile(crlfile);
}

void we_check_damiens_cert_in_crl(void)
{
	long result;
	char *crlfile = "carol.crl";
	char *issuercert = "carol.cer";
	char *certfile = "damien.cer";
	char *date;

	printf("\nWe check whether or not Damien's cert has been revoked...\n");

	result = X509_CheckCertInCRL(certfile, crlfile, issuercert, "", 0);
	if (result == PKI_X509_REVOKED)
		printf("Cert '%s' HAS BEEN REVOKED\n", certfile);
	else if (result == 0)
		printf("Cert '%s' has NOT been revoked\n", certfile);
	assert(result >= 0);

	/* But it was still valid on the first of March 2010 */
	date = "2010-03-01";
	result = X509_CheckCertInCRL(certfile, crlfile, "", date, 0);
	if (result == PKI_X509_REVOKED)
		printf("Cert '%s' WAS REVOKED as at %s\n", certfile, date);
	else if (result == 0)
		printf("Cert '%s' had NOT been revoked as at %s\n", certfile, date);
	assert(result >= 0);
}

void test_byte_encoding(void)
{
	long ulen, llen;
	unsigned char *utf8, *newlatin;
	/*
	 Set up a byte array with the following 4 characters encoded in Latin-1
	  U+0061 LATIN SMALL LETTER A
	  U+00E9 LATIN SMALL LETTER E WITH ACUTE
	  U+00F1 LATIN SMALL LETTER N WITH TILDE
	  U+0062 LATIN SMALL LETTER B
	*/
	unsigned char latin1[] = { 0x61, 0xe9, 0xf1, 0x62 };

	printf("\nWe change the byte encoding of a string from Latin-1 to UTF-8 and back again...\n");
	llen = sizeof(latin1);
	pr_byteshex("Latin-1=(0x)", latin1, llen, " ");
	printf("(%ld bytes)\n", llen);
	/* Convert to UTF-8 */
	ulen = CNV_ByteEncoding(NULL, 0, latin1, llen, PKI_CNV_UTF8_FROM_LATIN1);
	assert(ulen > 0);
	utf8 = malloc(ulen);
	ulen = CNV_ByteEncoding(utf8, ulen, latin1, llen, PKI_CNV_UTF8_FROM_LATIN1);
	pr_byteshex("UTF-8  =(0x)", utf8, ulen, " ");
	printf("(%ld bytes)\n", ulen);
	/* and convert back to Latin-1 */
	llen = CNV_ByteEncoding(NULL, 0, utf8, ulen, PKI_CNV_LATIN1_FROM_UTF8);
	assert(llen > 0);
	newlatin = malloc(llen);
	llen = CNV_ByteEncoding(newlatin, llen, utf8, ulen, PKI_CNV_LATIN1_FROM_UTF8);
	pr_byteshex("Latin-1=(0x)", newlatin, llen, " ");
	printf("(%ld bytes)\n", llen);

	free(utf8);
	free(newlatin);
}

void test_utf8bytes_from_latin1(void)
{
	long utflen, latinlen, status;
	unsigned char *cpu;

	const char *latin1data = "A��o � r�pida";	// This contains 4 non-ASCII chars
	printf("\nWe change a Latin-1 string into UTF-8-encoded bytes...\n");
	latinlen = (long)strlen(latin1data);
	printf("Latin-1=\"%s\" (%ld characters)\n", latin1data, latinlen);
	pr_byteshex("Latin-1=(0x)", latin1data, latinlen, " ");
	printf("(%ld bytes)\n", latinlen);

	utflen = CNV_UTF8BytesFromLatin1(NULL, 0, latin1data);
	printf("CNV_UTF8BytesFromLatin1 returns %ld\n", utflen);
	assert(utflen >= 0);
	cpu = malloc(utflen+1);
	assert(cpu != NULL);
	utflen = CNV_UTF8BytesFromLatin1(cpu, utflen, latin1data);
	pr_byteshex("UTF-8=(0x)", cpu, utflen, " ");
	printf("(%ld bytes)\n", utflen);
	
	printf("Now check we have valid UTF-8-encoded bytes...\n");
	status = CNV_CheckUTF8Bytes(cpu, utflen);
	printf("CNV_CheckUTF8Bytes returns %ld (expected 2)\n", status);
	assert(status > 0);
	free(cpu);
}

void check_asn1(void)
{
	char *fname = "ann.pub";
	char *outfile = "ann.pub-out.txt";
	char asn1type[PKI_ASN1_TYPE_MAXCHARS+1];
	long r;

	printf("\nExamine Ann's key file, which is ASN.1 encoded...\n");
	printf("FILE: %s\n", fname);
	// What sort of ASN.1 file is it?
	r = ASN1_Type(asn1type, sizeof(asn1type) - 1, fname, 0);
	assert(r > 0);
	printf("ASN1_Type=>'%s'\n", asn1type);
	// Dump details
	r = ASN1_TextDump(outfile, fname, 0);
	assert(r == 0);
	printf("ASN_TextDump:\n");
	pr_textfile(outfile);

}

void ecc_makekeys_and_sign(void)
{
	unsigned char *data;
	long datalen;
	char *keyfile = "prime256v1.p8";
	char *pubkeyfile = "prime256v1.pub";
	char *sig = NULL;
	long siglen;
	long r;
	char *password = "password";	// CAUTION: do not hardcode passwords!
	char *keybuf = NULL;
	char qbuf[64];
	long nchars;

	printf("\nCreate an ECC key pair and sign data using ECDSA in various formats...\n");

	// CREATE AN ECC KEY PAIR USING THE NIST P-256 curve
	r = ECC_MakeKeys(pubkeyfile, keyfile, "prime256v1", password, "", 0);
	printf("ECC_MakeKeys returns %ld (expected 0)\n", r);

	// QUERY THE KEY FOR INFO
	// READ IN FIRST TO INTERNAL KEY STRING
	nchars = ECC_ReadPrivateKey(NULL, 0, keyfile, password, 0);
	assert(nchars > 0);
	keybuf = malloc(nchars + 1);
	assert(keybuf);
	nchars = ECC_ReadPrivateKey(keybuf, nchars, keyfile, password, 0);
	// QUERY THE INTERNAL KEY STRING FOR INFO
	r = ECC_QueryKey(NULL, 0, keybuf, "keyBits", 0);
	printf("keyBits: %ld\n", r);
	r = ECC_QueryKey(qbuf, sizeof(qbuf)-1, keybuf, "curveName", 0);
	printf("curveName: %s\n", qbuf);
	// We are done with the internal key string
	free(keybuf);

	// DATA TO BE SIGNED IS THE ASCII STRING "abc"
	data = (unsigned char*)"abc";
	datalen = (long)strlen((char*)data);
	pr_byteshex("DATA=0x", data, datalen, "\n");

	// SIGN THE DATA...
	siglen = SIG_SignData(NULL, 0, data, datalen, keyfile, password, "ecdsaWithSHA256", 0);
	printf("SIG_SignData returns %ld (expected >0)\n", siglen);
	if (siglen < 0) {
		disp_error(siglen);
	}
	assert(siglen > 0);

	sig = malloc(siglen + 1);
	assert(sig);
	siglen = SIG_SignData(sig, siglen, data, datalen, keyfile, password, "ecdsaWithSHA256", 0);
	printf("SIG=[%s]\n", sig);

	// VERIFY THE SIGNATURE...
	printf("Verifying signature:\n");
	r = SIG_VerifyData(sig, data, datalen, pubkeyfile, "ecdsaWithSHA256", 0);
	printf("SIG_VerifyData returns %ld (expected 0)\n", r);
	assert(0 == r);

	// CLEAN UP...
	if (sig) free(sig);

	// NOW DO AGAIN WITH SIGNATURE ENCODED IN BASE64URL
	printf("BASE64URL...\n");
	siglen = SIG_SignData(NULL, 0, data, datalen, keyfile, password, "ecdsaWithSHA256", PKI_ENCODE_BASE64URL);
	printf("SIG_SignData returns %ld (expected >0)\n", siglen);
	if (siglen < 0) {
		disp_error(siglen);
	}
	assert(siglen > 0);

	sig = malloc(siglen + 1);
	assert(sig);
	siglen = SIG_SignData(sig, siglen, data, datalen, keyfile, password, "ecdsaWithSHA256", PKI_ENCODE_BASE64URL);
	printf("SIG=[%s]\n", sig);

	// VERIFY THE SIGNATURE...
	printf("Verifying signature:\n");
	r = SIG_VerifyData(sig, data, datalen, pubkeyfile, "ecdsaWithSHA256", 0);
	printf("SIG_VerifyData returns %ld (expected 0)\n", r);
	assert(0 == r);

	// CLEAN UP...
	if (sig) free(sig);

	// AND AGAIN IN HEX USING DETERMINISTIC METHOD OF [RFC6979] (= always the same signature value) 
	printf("HEX...\n");
	siglen = SIG_SignData(NULL, 0, data, datalen, keyfile, password, "ecdsaWithSHA256", 
		PKI_ENCODE_HEX | PKI_SIG_DETERMINISTIC);
	printf("SIG_SignData returns %ld (expected >0)\n", siglen);
	if (siglen < 0) {
		disp_error(siglen);
	}
	assert(siglen > 0);

	sig = malloc(siglen + 1);
	assert(sig);
	siglen = SIG_SignData(sig, siglen, data, datalen, keyfile, password, "ecdsaWithSHA256", 
		PKI_ENCODE_HEX | PKI_SIG_DETERMINISTIC);
	printf("SIG=[%s]\n", sig);

	// VERIFY THE SIGNATURE...
	printf("Verifying signature:\n");
	r = SIG_VerifyData(sig, data, datalen, pubkeyfile, "ecdsaWithSHA256", 0);
	printf("SIG_VerifyData returns %ld (expected 0)\n", r);
	assert(0 == r);

	// CLEAN UP...
	if (sig) free(sig);

	// HEX USING DETERMINISTIC METHOD OF [RFC6979] WITH SIGNATURE IN ASN1-DER FORM
	printf("HEX + ASN1DER...\n");
	siglen = SIG_SignData(NULL, 0, data, datalen, keyfile, password, "ecdsaWithSHA256", 
		PKI_ENCODE_HEX | PKI_SIG_DETERMINISTIC | PKI_SIG_ASN1DER);
	printf("SIG_SignData returns %ld (expected >0)\n", siglen);
	if (siglen < 0) {
		disp_error(siglen);
	}
	assert(siglen > 0);

	sig = malloc(siglen + 1);
	assert(sig);
	siglen = SIG_SignData(sig, siglen, data, datalen, keyfile, password, "ecdsaWithSHA256", 
		PKI_ENCODE_HEX | PKI_SIG_DETERMINISTIC | PKI_SIG_ASN1DER);
	printf("SIG=[%s]\n", sig);

	// VERIFY THE SIGNATURE...
	printf("Verifying signature:\n");
	r = SIG_VerifyData(sig, data, datalen, pubkeyfile, "ecdsaWithSHA256", 0);
	printf("SIG_VerifyData returns %ld (expected 0)\n", r);
	assert(0 == r);

	// CLEAN UP...
	if (sig) free(sig);

}

void ecc_bitcoin_raw_verify(void)
{
	char *keybuf;
	long nchars, ret;
	const char *keyhex;
	const char *curvename;
	char *sigbuf;
	unsigned char digest[PKI_SHA256_BYTES];
	char *query;

	unsigned char raw_tx[] = { 0x01, 0x00, 0x00, 0x00, 0x01, 0xc9, 0x97, 0xa5, 0xe5, 0x6e, 0x10, 0x41, 0x02, 0xfa, 0x20, 0x9c, 0x6a, 0x85, 0x2d, 0xd9, 0x06, 0x60, 0xa2, 0x0b, 0x2d, 0x9c, 0x35, 0x24, 0x23, 0xed, 0xce, 0x25, 0x85, 0x7f, 0xcd, 0x37, 0x04, 0x00, 0x00, 0x00, 0x00, 0x43, 0x41, 0x04, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, 0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e, 0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca, 0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xb2, 0xe0, 0xea, 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64, 0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9, 0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56, 0xb4, 0x12, 0xa3, 0xac, 0xff, 0xff, 0xff, 0xff, 0x02, 0x00, 0xca, 0x9a, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x43, 0x41, 0x04, 0xae, 0x1a, 0x62, 0xfe, 0x09, 0xc5, 0xf5, 0x1b, 0x13, 0x90, 0x5f, 0x07, 0xf0, 0x6b, 0x99, 0xa2, 0xf7, 0x15, 0x9b, 0x22, 0x25, 0xf3, 0x74, 0xcd, 0x37, 0x8d, 0x71, 0x30, 0x2f, 0xa2, 0x84, 0x14, 0xe7, 0xaa, 0xb3, 0x73, 0x97, 0xf5, 0x54, 0xa7, 0xdf, 0x5f, 0x14, 0x2c, 0x21, 0xc1, 0xb7, 0x30, 0x3b, 0x8a, 0x06, 0x26, 0xf1, 0xba, 0xde, 0xd5, 0xc7, 0x2a, 0x70, 0x4f, 0x7e, 0x6c, 0xd8, 0x4c, 0xac, 0x00, 0x28, 0x6b, 0xee, 0x00, 0x00, 0x00, 0x00, 0x43, 0x41, 0x04, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, 0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e, 0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca, 0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xb2, 0xe0, 0xea, 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64, 0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9, 0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56, 0xb4, 0x12, 0xa3, 0xac, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 };
	unsigned char signature[] = { 0x30, 0x44, 0x02, 0x20, 0x4e, 0x45, 0xe1, 0x69, 0x32, 0xb8, 0xaf, 0x51, 0x49, 0x61, 0xa1, 0xd3, 0xa1, 0xa2, 0x5f, 0xdf, 0x3f, 0x4f, 0x77, 0x32, 0xe9, 0xd6, 0x24, 0xc6, 0xc6, 0x15, 0x48, 0xab, 0x5f, 0xb8, 0xcd, 0x41, 0x02, 0x20, 0x18, 0x15, 0x22, 0xec, 0x8e, 0xca, 0x07, 0xde, 0x48, 0x60, 0xa4, 0xac, 0xdd, 0x12, 0x90, 0x9d, 0x83, 0x1c, 0xc5, 0x6c, 0xbb, 0xac, 0x46, 0x22, 0x08, 0x22, 0x21, 0xa8, 0x76, 0x8d, 0x1d, 0x09 };

	printf("\nECC example from <%s>\n", "https://en.bitcoin.it/wiki/OP_CHECKSIG");
	curvename = "secp256k1";
	printf("curve: %s\n", curvename);
	keyhex = "0411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3";
	printf("public key: %s\n", keyhex);

	// READ IN PUBLIC KEY TO INTERNAL KEY STRING
	nchars = ECC_ReadKeyByCurve(NULL, 0, keyhex, curvename, 0);
	printf("ECC_ReadKeyByCurve returns %ld\n", nchars);
	assert(nchars > 0);
	keybuf = malloc(nchars + 1);
	assert(keybuf);
	nchars = ECC_ReadKeyByCurve(keybuf, nchars, keyhex, curvename, 0);

	// QUERY THE KEY STRING
	query = "keyBits";
	ret = ECC_QueryKey(NULL, 0, keybuf, query, 0);
	printf("ECC_QueryKey('%s')=%ld\n", query, ret);
	query = "isPrivate";
	ret = ECC_QueryKey(NULL, 0, keybuf, query, 0);
	printf("ECC_QueryKey('%s')=%ld\n", query, ret);

	// CONVERT SIGNATURE TO BASE64-ENCODED STRING
	pr_byteshex("SIG (HEX):\n", signature, sizeof(signature), "\n");
	nchars = CNV_B64StrFromBytes(NULL, 0, signature, sizeof(signature));
	assert(nchars > 0);
	sigbuf = malloc(nchars + 1);
	assert(sigbuf);
	nchars = CNV_B64StrFromBytes(sigbuf, nchars, signature, sizeof(signature));
	printf("SIG (B64): %s\n", sigbuf);

	pr_byteshex("RAW_TX:\n", raw_tx, sizeof(raw_tx), "\n");
	// COMPUTE THE DOUBLE SHA-256 DIGEST OF RAW DATA (NB this is "proper" order, not reversed)
	HASH_Bytes(digest, sizeof(digest), raw_tx, sizeof(raw_tx), PKI_HASH_SHA256 | PKI_HASH_DOUBLE);
	pr_byteshex("SHA256(SHA256(RAW_TX)):\n", digest, sizeof(digest), "\n");

	// VERIFY AGAINST THE DIGEST, NOT THE RAW DATA, USING THE INTERNAL KEY STRING
	ret = SIG_VerifyData(sigbuf, digest, sizeof(digest), keybuf, "ecdsawithSHA256", PKI_SIG_USEDIGEST);
	printf("SIG_VerifyData returns %ld (expected 0)\n", ret);
	if (ret != 0) disp_error(ret);

	free(keybuf);
	free(sigbuf);
}

void byte_utilities(void)
{
	long nbytes, nchars;
	unsigned char *bytes;
	char *strdata;
	char *buf;
	unsigned char buffer[4];
	//unsigned char bigbuf[] = { 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0xba, 0xbe };
	long r;
	long num;

	printf("\nBYTE UTILITIES AND BASE58 TESTS...\n");

	// CONVERT BASE58 TO BYTES AND HEX
	strdata = "19AGpMSqrFh9dWJbpcEyfEGxueNkzw53Hn";
	printf("INPUT: '%s'\n", strdata);
	// Convert to bytes
	nbytes = CNV_Base58ToBytes(NULL, 0, strdata);
	printf("CNV_Base58ToBytes -> %ld bytes\n", nbytes);
	assert(nbytes >= 0);
	bytes = malloc(nbytes);
	assert(bytes != NULL);
	nbytes = CNV_Base58ToBytes(bytes, nbytes, strdata);
	pr_byteshex("BYTES: ", bytes, nbytes, "\n");

	// Convert to hex
	nchars = CNV_HexStrFromBytes(NULL, 0, bytes, nbytes);
	buf = malloc(nchars + 1);
	assert(buf);
	nchars = CNV_HexStrFromBytes(buf, nchars, bytes, nbytes);
	printf("HEX: %s\n", buf);

	// Free buffers
	free(buf);
	free(bytes);


	// CONVERT BETWEEN ARRAY OF 4 BYTES AND 32-BIT INTEGER
	nbytes = 4;
	r = CNV_NumToBytes(buffer, nbytes, 0xdeadbeef, 0);
	pr_byteshex("CNV_NumToBytes(0xdeadbeef)=x'", buffer, nbytes, "'\n");
	num = CNV_NumFromBytes(buffer, nbytes, 0);
	printf("CNV_NumFromBytes(b)=0x%08lx\n", num);
	assert(0xdeadbeef == num);

	r = CNV_NumToBytes(buffer, nbytes, 0xdeadbeef, PKI_CNV_LITTLE_ENDIAN);
	pr_byteshex("CNV_NumToBytes(0xdeadbeef, LE)=x'", buffer, nbytes, "'\n");
	num = CNV_NumFromBytes(buffer, nbytes, PKI_CNV_LITTLE_ENDIAN);
	printf("CNV_NumFromBytes(b, LE)=0x%08lx\n", num);
	assert(0xdeadbeef == num);

	// REVERSE BYTE ARRAY
	pr_byteshex("BYTES BEFORE REVERSE: ", buffer, nbytes, "\n");
	r = CNV_ReverseBytes(buffer, buffer, nbytes);
	pr_byteshex("BYTES AFTER REVERSE:  ", buffer, nbytes, "\n");
	num = CNV_NumFromBytes(buffer, nbytes, 0);
	printf("CNV_NumFromBytes(b)=0x%08lx\n", num);

}

void compress_data(void)
{
	const char *datastr = "hello, hello, hello. This is a 'hello world' message for the world, repeat, for the world.";
	unsigned char *comprdata, *uncomprdata;
	long dlen, clen, ulen;

	printf("\nCOMPRESSION TESTS...\n");

	dlen = (long)strlen(datastr);
	printf("Input: '%s'\n", datastr);
	/* Find required output length */
	clen = COMPR_Compress(NULL, 0, (unsigned char *)datastr, dlen, 0);
	printf("COMPR_Compress returns %ld (expected +ve)\n", clen);
	/* Allocate memory */
	comprdata = malloc(clen);
	/* Compress it */
	clen = COMPR_Compress(comprdata, clen, (unsigned char *)datastr, dlen, 0);
	pr_byteshex("Compressed data:\n", comprdata, clen, "\n");

	printf("Now uncompress it...\n");
	ulen = COMPR_Uncompress(NULL, 0, comprdata, clen, 0);
	printf("COMPR_Uncompress returns %ld (expected +ve)\n", ulen);
	/* Allocate memory - NB extra byte for zero terminating byte to be added later */
	uncomprdata = malloc(ulen + 1);
	/* Uncompress it */
	ulen = COMPR_Uncompress(uncomprdata, ulen, comprdata, clen, 0);
	pr_byteshex("Uncompressed data:\n", uncomprdata, ulen, "\n");
	/* Note: output is a byte arry and not a zero-terminated string, so we do it now */
	uncomprdata[ulen] = '\0';
	printf("Uncompressed string: '%s'\n", uncomprdata);
	assert(dlen == ulen && 0 == memcmp(uncomprdata, datastr, dlen));

	/* Free allocated memory */
	free(comprdata);
	free(uncomprdata);
}

void read_cert_from_pfx_and_p7c(void)
{
	/* Input data is in a string in PEM format.
	 * Output is extracted directly into a string buffer in memory.
	 */
	const char *p7str =	/* bob.p7b (contains 2 X.509 certs: BobRSA and CarlRSA) */
		"-----BEGIN PKCS7-----"
		"MIIERQYJKoZIhvcNAQcCoIIENjCCBDICAQExADALBgkqhkiG9w0BBwGgggQaMIICJzCCAZCgAwIB"
		"AgIQRjRrx4AAVrwR024uzV1x0DANBgkqhkiG9w0BAQUFADASMRAwDgYDVQQDEwdDYXJsUlNBMB4X"
		"DTk5MDkxOTAxMDkwMloXDTM5MTIzMTIzNTk1OVowETEPMA0GA1UEAxMGQm9iUlNBMIGfMA0GCSqG"
		"SIb3DQEBAQUAA4GNADCBiQKBgQCp4WeYPznVX/Kgk0FepnmJhcg1XZqRW/sdAdoZcCYXD72lItA1"
		"hW16mGYUQVzPt7cIOwnJkbgZaTdt+WUee9mpMySjfzu7r0YBhjY0MssHA1lS/IWLMQS4zBgIFEjm"
		"Txz7XWDE4FwfU9N/U9hpAfEF+Hpw0b6Dxl84zxwsqmqn6wIDAQABo38wfTAMBgNVHRMBAf8EAjAA"
		"MA4GA1UdDwEB/wQEAwIFIDAfBgNVHSMEGDAWgBTp4JAnrHggeprTTPJCN04irp44uzAdBgNVHQ4E"
		"FgQU6PS4Z9izlqQq8xGqKdOVWoYWtCQwHQYDVR0RBBYwFIESQm9iUlNBQGV4YW1wbGUuY29tMA0G"
		"CSqGSIb3DQEBBQUAA4GBAHuOZsXxED8QIEyIcat7QGshM/pKld6dDltrlCEFwPLhfirNnJOIh/uL"
		"t359QWHh5NZt+eIEVWFFvGQnRMChvVl52R1kPCHWRbBdaDOS6qzxV+WBfZjmNZGjOd539OgcOync"
		"f1EHl/M28FAK3Zvetl44ESv7V+qJba3JiNiPzyvTMIIB6zCCAVSgAwIBAgIQRjRrx4AAVrwR024u"
		"n/JQIDANBgkqhkiG9w0BAQUFADASMRAwDgYDVQQDEwdDYXJsUlNBMB4XDTk5MDgxODA3MDAwMFoX"
		"DTM5MTIzMTIzNTk1OVowEjEQMA4GA1UEAxMHQ2FybFJTQTCBnzANBgkqhkiG9w0BAQEFAAOBjQAw"
		"gYkCgYEA5Ev/GLgkV/R3/25ze5NxXLwzGpKSciPYQUbQzRE6BLOOr4KdvVEeF3rydiwrhjmnvdeN"
		"GlPs5ADV6OyiNrHt4lDiMgmKP5+ZJY+4Tqu5fdWWZdoWoMW+Dq5EW+9e9Kcpy4LdrETpqpOUKQ74"
		"GNbIV17ydsTyEWA4uRs8HZfJavECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E"
		"BAMCAYYwHQYDVR0OBBYEFOngkCeseCB6mtNM8kI3TiKunji7MA0GCSqGSIb3DQEBBQUAA4GBALee"
		"1ATT7Snk/4mJFS5M2wzwSA8yYe7EBOwSXS3/D2RZfgrD7Rj941ZAN6cHtfA4EmFQ7e/dP+MLuGGl"
		"pJs85p6cVJq2ldbabDu1LUU1nUkBdvq5uTH5+WsSU6D1FGCbfco+8lNrsDdvreZ019v6WuoUQWNd"
		"zb7IDsHaao1TNBgCMQA="
		"-----END PKCS7-----";
	const char *pfxstr =	/* bob.pfx (password="password") */
		"-----BEGIN PKCS12-----"
		"MIIGhAIBAzCCBkoGCSqGSIb3DQEHAaCCBjsEggY3MIIGMzCCAv8GCSqGSIb3DQEH"
		"BqCCAvAwggLsAgEAMIIC5QYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQIawUA"
		"VTFvAiECAggAgIICuNwEuFcRnZamZyMyIn+vH+wC5BVUtZAWNrlIqToezF7cYqt/"
		"18+HXB/46nllz+qUD3Dv9rS78MnPeAM47afFRTricHsiOpE+2eXf32lxduoF5+CL"
		"S3S7TAhRUMp2Fh18LlukzK9lY67BGfU9Y3yCukTmwVXqe49dkj8y9JjVJhXnoc2c"
		"7eOk3o5RjXHFsAMHwirqdsESHstrDZYLMVGw5HnAamY7zQd8WUpIweAFaEDLJfyz"
		"qY1/LTL/txvZ9VQ/B/36HKyEpoIvuH6iOCBkebpJwWSkkffuVFbUfMLguMztL/sf"
		"+jE2NiuljSBJ9pTNsZziZWERb6CxZH0a2xkkBTciXM5Dl5efWL0GmBg+aJSIyh+G"
		"w5W8Q7gmnH6H9myszvW9uYv/epwCbIpHd0dRHPbL3fR4KGhFexq24tAG86tDqPKb"
		"6H6n0lSA+Oq46SwZ00xIFpVcFaO/8yVqf6+JRDGoZ55aAZF6OCi7R1GvI+6pzz37"
		"pvP7SWfqVSuXCTNQq9uKw97SH5YftQ9hkELQ4vHCjFh4UJSBUCZgDtqR1uB/+44H"
		"5UpP8KvbETaOFJszMxsqXBMqc1uEODSNg+EHEx+yg7Bx1CcNrm+6rtThC49+ow18"
		"HDMxbn3lAw1ooblANvSzR4YTt68N/4dtwROOdXjwKzyg03qWK2sJaiH5LzbB5MMm"
		"rdAChb9dLoRKBN2LREob7KRKEs6v51IW1yq4UCwSmpP+RbchZwIoKVXx/MYKjVqz"
		"GfZAgBRpXEq/KH/8R+ttFPKdab2GAEjd7hIOmetp5einQmK4C7JYE6Uyabf1IImt"
		"VhBw2dGU3GiM2zSIGqCx3bmYETZheMTAV9MMVUYe8gQeEpbXM4GAnwX0wpS0aYap"
		"zGeA/62X2nFh21eRHVzUcf0miXVvyOy6a1vj6O6N5F1jVaCV3jCCAywGCSqGSIb3"
		"DQEHAaCCAx0EggMZMIIDFTCCAxEGCyqGSIb3DQEMCgECoIICpjCCAqIwHAYKKoZI"
		"hvcNAQwBAzAOBAjw/dx4SlLcWwICCAAEggKALm91I8gYuPpRTCSn5pN4OQBLbI6j"
		"SW+9FGeNYvOy/+Pt3Oq0i15ZXZZez7dP8rdb0tmTCSZwVPIwtJRKxYUNaTppUTWZ"
		"hXhnmeTMtSZpFuKmo6UhW8lGUcg45sO5UKUtdH0/UgewaSUfV4L06vp4j7Fugwbp"
		"666seJJ/9vQwMAxoqj0blxNNmASAcW7yj/lA2/p4KuGlnGkv4MSW5ViH7T24VeFX"
		"TzyFFR7UR1Nw9Blr5jdr7b2rZSdTj0GeHZ/L3FksFWJocl8PEEL4ZdVscbvO+l7v"
		"tbeBz0y9TDr/HUwt2tfqXgjckVVoJhmsczJXrG5Ai+brKnGQ7R5uIpIsqd9O6EpG"
		"68VMMGA5iSKsLYtibieqom8mRO00sFiQharxONEdveY+3O98nG6xzHlaBdNbxVo3"
		"8Y+4LK6Gc81dUWYwss3ajdiJWe0+TYQjMPF72eWctcQAoTxITpd/j6rD7EmvLVyP"
		"IR46L4w6Gb/uz5G1T1UiLoh9luM1nRKKICyo2XllZDNO0msaub7DH1xzJzEy2OT9"
		"cwChqYfKKeWEE2BWL699fmq5RMCbIQVtE2bJDP8obu9j6HLskCiZcJm6nC7IKS1p"
		"Q2BA/JJVKxC8ADuLOAOdicWquDd8MWL5a9HpXd5TtUlfiRecTw8IRozTLaoDVlha"
		"YNGPzwkjL9zZ+Up5Uy6HHXMDb0aD0fgvMqdAspB1+Xlt2RgP6CnEH2hwQqGFoA8T"
		"tijeS+DtdMy8BxJ7g1fiEH0+4UISl1vymjPI1MJCI1VlFLvpjZvKHluwjgp1SHk3"
		"tFRJLJ8a/eApvmscKXSlxcYz+5Bv8dxPGdhO/KOLQS7XZ4a8VSg977WS1jFYMCMG"
		"CSqGSIb3DQEJFTEWBBRj8EbS3XBC5R/cJqUR73yB6mItizAxBgkqhkiG9w0BCRQx"
		"JB4iAEIAbwBiACcAcwAgAGYAcgBpAGUAbgBkAGwAeQAgAEkARDAxMCEwCQYFKw4D"
		"AhoFAAQUaHSMUJ415FfKGv3cZpwloKDmqgYECAreM3EkHVjCAgIIAA=="
		"-----END PKCS12-----";
	char *certbuf;
	long nchars, ncerts, index, r;
	char querybuf[128];

	printf("\nREAD CERTS IN MEMORY...\n");

	/* Get count of certs in P7 chain */
	index = 0;
	ncerts = X509_ReadCertStringFromP7Chain(NULL, 0, p7str, index, 0);
	printf("X509_ReadCertStringFromP7Chain(STRING, %ld) returns ncerts = %ld\n", index, ncerts);
	if (ncerts < 0) disp_error(ncerts);
	assert(ncerts > 0);
	/* Read all certs */
	for (index = 1; index <= ncerts; index++) {
		/* Find required length of buffer */
		nchars = X509_ReadCertStringFromP7Chain(NULL, 0, p7str, index, 0);
		printf("X509_ReadCertStringFromP7Chain(%ld) returns %ld\n", index, nchars);
		if (nchars < 0) disp_error(nchars);
		assert(nchars > 0);
		/* Allocate memory NB +1 for terminating null */
		certbuf = malloc(nchars + 1);
		nchars = X509_ReadCertStringFromP7Chain(certbuf, nchars, p7str, index, 0);
		printf("%s\n", certbuf);
		/* Query the cert for subject name */
		r = X509_QueryCert(querybuf, sizeof(querybuf) - 1, certbuf, "subjectName", 0);
		printf("subjectName='%s'\n", querybuf);
		/* Compute the SHA-1 thumbprint */
		r = X509_CertThumb(certbuf, querybuf, sizeof(querybuf) - 1, 0);
		printf("SHA-1(cert)=%s\n", querybuf);
		/* Free memory */
		free(certbuf);
	}

	/* Read in cert as string from PFX/P12 file */
	nchars = X509_ReadCertStringFromPFX(NULL, 0, pfxstr, "password", 0);
	printf("X509_ReadCertStringFromPFX() returns %ld\n", nchars);
	if (nchars < 0) disp_error(nchars);
	assert(nchars > 0);
	/* Allocate memory NB +1 for terminating null */
	certbuf = malloc(nchars + 1);
	nchars = X509_ReadCertStringFromPFX(certbuf, nchars, pfxstr, "password", 0);
	//printf("%s\n", certbuf);
	/* Query the cert for subject name */
	r = X509_QueryCert(querybuf, sizeof(querybuf) - 1, certbuf, "subjectName", 0);
	printf("subjectName='%s'\n", querybuf);
	/* Compute the SHA-1 thumbprint */
	r = X509_CertThumb(certbuf, querybuf, sizeof(querybuf) - 1, 0);
	printf("SHA-1(cert)=%s\n", querybuf);
	/* Free memory */
	free(certbuf);

}

void read_rsa_from_jws()
{
	long nchars;
	char *publickey;
	// RSA public key as a JSON string
	// Ref: RFC 7517 JSON Web Key (JWK) Appendix A.1
	const char *json = "{\"kty\":\"RSA\","
		"\"n\": \"0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx"
		"4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMs"
		"tn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2"
		"QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbI"
		"SD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqb"
		"w0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw\","
		"\"e\":\"AQAB\","
		"\"alg\":\"RS256\","
		"\"kid\":\"2011-04-29\"}";
	printf("\nREAD IN RSA KEY REPRESENTED AS JSON JWK...\n");
	printf("JSON key=%s\n", json);
	// How many chars?
	nchars = RSA_ReadAnyPublicKey(NULL, 0, json, 0);
	assert(nchars > 0);
	publickey = malloc(nchars + 1);
	// Read in public key
	nchars = RSA_ReadAnyPublicKey(publickey, nchars, json, 0);
	// Examine the key's properties
	printf("RSA key size = %ld bits\n", RSA_KeyBits(publickey));
	printf("Key hash code = 0x%08lX\n", RSA_KeyHashCode(publickey));

	free(publickey);
}

// New in [v20.0]

void cipher_encrypt_hex(void)
{
	char *testfn = "CIPHER_EncryptHex()";
	long nchars;
	// Section 8.1 of [SMIME-EX]
	char *algstr = "tdea-cbc";
	char *sHexKey = "737C791F25EAD0E04629254352F7DC6291E5CB26917ADA32";
	char *sHexIV = "B36B6BFB6231084E";
	char *sPlain = "5468697320736F6D652073616D706520636F6E74656E742E";
	//              T h i s _ s o m e _ s a m p e _ c o n t e n t .
	char *sCorrect = "d76fd1178fbd02f84231f5c1d2a2f74a4159482964f675248254223daf9af8e4";
	char *cthex, *chkhex;

	printf("\nENCRYPT DATA USING HEX-ENCODED PARAMETERS:\n");

	printf("ALG=%s\n", algstr);
	printf("KY=%s\n", sHexKey);
	printf("IV=%s\n", sHexIV);
	printf("PT=%s\n", sPlain);

	nchars = CIPHER_EncryptHex(NULL, 0, sPlain, sHexKey, sHexIV, algstr, 0);
	printf("CIPHER_EncryptHex returns %ld\n", nchars);
	if (nchars < 0) disp_error(nchars);
	assert(nchars >= 0);

	cthex = malloc(nchars + 1);
	nchars = CIPHER_EncryptHex(cthex, nchars, sPlain, sHexKey, sHexIV, algstr, 0);

	printf("CT=%s\n", cthex);
	printf("OK=%s\n", sCorrect);
	assert(stricmp(cthex, sCorrect) == 0);

	// Now decrypt back to plain text
	nchars = CIPHER_DecryptHex(NULL, 0, cthex, sHexKey, sHexIV, algstr, 0);
	printf("CIPHER_DecryptHex returns %ld\n", nchars);
	if (nchars < 0) disp_error(nchars);
	assert(nchars >= 0);
	chkhex = malloc(nchars + 1);
	nchars = CIPHER_DecryptHex(chkhex, nchars, cthex, sHexKey, sHexIV, algstr, 0);
	printf("P'=%s\n", chkhex, nchars);
	assert(stricmp(chkhex, sPlain) == 0);

	free(cthex);
	free(chkhex);

	printf("...AGAIN BUT PREFIXING THE IV TO THE CIPHERTEXT\n");
	algstr = "Aes128/CBC/OneAndZeroes";
	sHexKey = "0123456789ABCDEFF0E1D2C3B4A59687";
	sHexIV = "FEDCBA9876543210FEDCBA9876543210";
	sPlain = "4E6F77206973207468652074696D6520666F7220616C6C20676F6F64206D656E20746F";
	//       "Now is the time for all good men to"
	// Prefix IV to ciphertext in output
	//          <-------------IV---------------><----------------------------------------------CT---------------------------------------------->
	sCorrect = "FEDCBA9876543210FEDCBA9876543210C3153108A8DD340C0BCB1DFE8D25D2320EE0E66BD2BB4A313FB75C5638E9E1771D4CDA34FBFB7E74B321F9A2CF4EA61B";
	printf("ALG=%s\n", algstr);
	printf("KY=%s\n", sHexKey);
	printf("IV=%s\n", sHexIV);
	printf("PT=%s\n", sPlain);

	nchars = CIPHER_EncryptHex(NULL, 0, sPlain, sHexKey, sHexIV, algstr, PKI_IV_PREFIX);
	printf("CIPHER_EncryptHex(PKI_IV_PREFIX) returns %ld\n", nchars);
	if (nchars < 0) disp_error(nchars);
	assert(nchars >= 0);

	cthex = malloc(nchars + 1);
	nchars = CIPHER_EncryptHex(cthex, nchars, sPlain, sHexKey, sHexIV, algstr, PKI_IV_PREFIX);

	printf("CT=%s\n", cthex);
	printf("OK=%s\n", sCorrect);
	assert(stricmp(cthex, sCorrect) == 0);

	// Now decrypt back to plain text
	// NB No need for IV
	nchars = CIPHER_DecryptHex(NULL, 0, cthex, sHexKey, NULL, algstr, PKI_IV_PREFIX);
	printf("CIPHER_DecryptHex returns %ld\n", nchars);
	if (nchars < 0) disp_error(nchars);
	assert(nchars >= 0);
	chkhex = malloc(nchars + 1);
	nchars = CIPHER_DecryptHex(chkhex, nchars, cthex, sHexKey, sHexIV, algstr, PKI_IV_PREFIX);
	printf("P'=%s\n", chkhex, nchars);
	assert(stricmp(chkhex, sPlain) == 0);

	free(cthex);
	free(chkhex);

}

void sign_data_Ed25519(void)
{
	// Ref: [RFC8032] https://tools.ietf.org/html/rfc8032#section-7.1
	// -----TEST SHA(abc)

	const char *sigalg = "Ed25519";
	const char *prikeyhex = "833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42";
	const char *pubkeyhex = "ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf";
	// Message is the 64-byte SHA-512 hash of "abc"
	unsigned char msg[] = {
		0xDD, 0xAF, 0x35, 0xA1, 0x93, 0x61, 0x7A, 0xBA,
		0xCC, 0x41, 0x73, 0x49, 0xAE, 0x20, 0x41, 0x31,
		0x12, 0xE6, 0xFA, 0x4E, 0x89, 0xA9, 0x7E, 0xA2,
		0x0A, 0x9E, 0xEE, 0xE6, 0x4B, 0x55, 0xD3, 0x9A,
		0x21, 0x92, 0x99, 0x2A, 0x27, 0x4F, 0xC1, 0xA8,
		0x36, 0xBA, 0x3C, 0x23, 0xA3, 0xFE, 0xEB, 0xBD,
		0x45, 0x4D, 0x44, 0x23, 0x64, 0x3C, 0xE8, 0x0E,
		0x2A, 0x9A, 0xC9, 0x4F, 0xA5, 0x4C, 0xA4, 0x9F,
	};
	const int kSIGCHARS = 128;	// Expected length of signature in hex
	const char *sigok =
		"dc2a4459e7369633a52b1bf277839a00201009a3efbf3ecb69bea2186c26b58909351fc9ac90b3ecfdfbc7c66431e0303dca179c138ac17ad9bef1177331a704";
	long msglen;
	long nchars, r;
	char *intprikey = NULL;
	char *intpubkey = NULL;
	char *sigout = NULL;

	printf("\nSIGN DATA USING Ed25519:\n");
	printf("ALGORITHM: %s\n", sigalg);
	printf("SECRET KEY: %s\n", prikeyhex);
	printf("PUBLIC KEY: %s\n", pubkeyhex);
	pr_byteshex("MESSAGE:\n", msg, sizeof(msg), "\n");

	//// Read in private key from hex  (NB for Ed25519 need explicitly to identify as private key)
	nchars = ECC_ReadKeyByCurve(NULL, 0, prikeyhex, "Ed25519", PKI_ECC_PRIVATE_KEY);
	if (nchars < 0) disp_error(nchars);
	assert(nchars > 0);
	intprikey = malloc(nchars + 1);
	nchars = ECC_ReadKeyByCurve(intprikey, nchars, prikeyhex, "Ed25519", PKI_ECC_PRIVATE_KEY);
	printf("Private key has %ld bits\n", ECC_QueryKey(NULL, 0, intprikey, "KeyBits", 0));

	// Compute the signature value (encoded in hex)
	msglen = sizeof(msg);
	// -- get required length
	nchars = SIG_SignData(NULL, 0, msg, msglen, intprikey, "", sigalg, PKI_ENCODE_HEX);
	printf("SIG_SignData returns %ld (expected %d)\n", nchars, kSIGCHARS);
	if (nchars < 0) disp_error(nchars);
	assert(nchars > 0);
	// -- allocate output buffer
	sigout = malloc(nchars + 1);
	// -- do the business
	nchars = SIG_SignData(sigout, nchars, msg, msglen, intprikey, "", sigalg, PKI_ENCODE_HEX);
	printf("SIG=%s\n", sigout);
	printf("OK =%s\n", sigok);
	assert(0 == strcmp(sigout, sigok));

	//// Now verify using the public key
	nchars = ECC_ReadKeyByCurve(NULL, 0, pubkeyhex, "Ed25519", PKI_ECC_PUBLIC_KEY);
	if (nchars < 0) disp_error(nchars);
	assert(nchars > 0);
	intpubkey = malloc(nchars + 1);
	nchars = ECC_ReadKeyByCurve(intpubkey, nchars, pubkeyhex, "Ed25519", PKI_ECC_PUBLIC_KEY);
	printf("Public key has %ld bits\n", ECC_QueryKey(NULL, 0, intpubkey, "KeyBits", 0));
	r = SIG_VerifyData(sigout, msg, msglen, intpubkey, sigalg, 0);
	printf("SIG_VerifyData returns %ld (expecting 0 => OK)\n", r);
	if (r < 0) disp_error(r);
	assert(0 == r);

	if (intprikey) free(intprikey);
	if (intpubkey) free(intpubkey);
	if (sigout) free(sigout);

}

void x509_cert_25519(void)
{
	char buf[512];
	const long maxbufchars = sizeof(buf) - 1;	// NB minus one for string output
	long n, nchars;
	char *query;
	char *intkeystr = NULL;

	// Ref: [RFC8410] https://tools.ietf.org/html/rfc8410#section-10.2
	// 10.2.  Example X25519 Certificate
	// This is a self-*issued* certificate containing an X25519 public key (intended for ECDH key agreement)
	// but signed using the Ed25519 signature algorithm. Issued by the same entity but not self-*signed*.
	const char *certstr =
		"-----BEGIN CERTIFICATE-----"
		"MIIBLDCB36ADAgECAghWAUdKKo3DMDAFBgMrZXAwGTEXMBUGA1UEAwwOSUVURiBUZX"
		"N0IERlbW8wHhcNMTYwODAxMTIxOTI0WhcNNDAxMjMxMjM1OTU5WjAZMRcwFQYDVQQD"
		"DA5JRVRGIFRlc3QgRGVtbzAqMAUGAytlbgMhAIUg8AmJMKdUdIt93LQ+91oNvzoNJj"
		"ga9OukqY6qm05qo0UwQzAPBgNVHRMBAf8EBTADAQEAMA4GA1UdDwEBAAQEAwIDCDAg"
		"BgNVHQ4BAQAEFgQUmx9e7e0EM4Xk97xiPFl1uQvIuzswBQYDK2VwA0EAryMB/t3J5v"
		"/BzKc9dNZIpDmAgs3babFOTQbs+BolzlDUwsPrdGxO3YNGhW7Ibz3OGhhlxXrCe1Cg"
		"w1AH9efZBw=="
		"-----END CERTIFICATE-----";

	printf("\nEXAMINE AN EXAMPLE X25519 CERTIFICATE:\n");
	// Query this certificate
	query = "subjectName";
	n = X509_QueryCert(buf, maxbufchars, certstr, query, 0);
	printf("%s='%s'\n", query, buf);
	query = "signatureAlgorithm";
	n = X509_QueryCert(buf, maxbufchars, certstr, query, 0);
	printf("%s='%s'\n", query, buf);
	query = "subjectPublicKeyAlgorithm";
	n = X509_QueryCert(buf, maxbufchars, certstr, query, 0);
	printf("%s='%s'\n", query, buf);
	query = "keyUsageString";
	n = X509_QueryCert(buf, maxbufchars, certstr, query, 0);
	printf("%s='%s'\n", query, buf);

	printf("Read in the public key from this certificate...\n");
	nchars = ECC_ReadPublicKey(NULL, 0, certstr, 0);
	printf("ECC_ReadPublicKey returns %ld\n", nchars);
	if (nchars < 0) disp_error(nchars);
	assert(nchars > 0);
	intkeystr = malloc(nchars + 1);
	nchars = ECC_ReadPublicKey(intkeystr, nchars, certstr, 0);

	// Query this public key for details...
	query = "curveName";
	n = ECC_QueryKey(buf, maxbufchars, intkeystr, query, 0);
	printf("%s='%s'\n", query, buf);
	query = "keyBits";
	n = ECC_QueryKey(buf, maxbufchars, intkeystr, query, 0);
	printf("%s=%ld\n", query, n);	// NB `n` contains the result directly
	query = "publicKey";
	n = ECC_QueryKey(buf, maxbufchars, intkeystr, query, 0);
	printf("%s='%s'\n", query, buf);


	if (intkeystr) free(intkeystr);
}

void ecdh_p256()
{
	long nchars, nbytes;
	char *ourprikey = NULL;
	char *theirpubkey = NULL;
	unsigned char *lpZZ = NULL;

	// Ref: CAVS 14.1 ECC CDH Primitive (SP800 - 56A Section 5.7.1.2)
	// Test Information for "testecccdh" ecccdhtestvectors.zip
	// [P-256]
	printf("\nECDH SHARED SECRET USING P-256:\n");
	// Our private key is dUIT:
	const char *ourPrivateKeyHex = "7d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534";
	// Their public key as hex is "04" || QCAVSx || QCAVSy
	const char *theirPublicKeyHex = "04" "700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287"
		"db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac";
	// Correct expected result "46FC62106420FF012E54A434FBDD2D25CCC5852060561E68040DD7778997BD7B"
	const unsigned char zzOK[] = {
		0x46, 0xFC, 0x62, 0x10, 0x64, 0x20, 0xFF, 0x01,
		0x2E, 0x54, 0xA4, 0x34, 0xFB, 0xDD, 0x2D, 0x25,
		0xCC, 0xC5, 0x85, 0x20, 0x60, 0x56, 0x1E, 0x68,
		0x04, 0x0D, 0xD7, 0x77, 0x89, 0x97, 0xBD, 0x7B,
	};

	// Read in our private key to an internal string
	nchars = ECC_ReadKeyByCurve(NULL, 0, ourPrivateKeyHex, "P-256", 0);
	printf("ECC_ReadKeyByCurve returns %ld (expecting +ve)\n", nchars);
	if (nchars < 0) disp_error(nchars);
	assert(nchars > 0);
	ourprikey = malloc(nchars + 1);
	nchars = ECC_ReadKeyByCurve(ourprikey, nchars, ourPrivateKeyHex, "P-256", 0);
	printf("Our private key has %ld bits\n", ECC_QueryKey(NULL, 0, ourprikey, "keyBits", 0));

	// Read in their public key to an internal string
	nchars = ECC_ReadKeyByCurve(NULL, 0, theirPublicKeyHex, "P-256", 0);
	printf("ECC_ReadKeyByCurve returns %ld (expecting +ve)\n", nchars);
	if (nchars < 0) disp_error(nchars);
	assert(nchars > 0);
	theirpubkey = malloc(nchars + 1);
	nchars = ECC_ReadKeyByCurve(theirpubkey, nchars, theirPublicKeyHex, "P-256", 0);
	printf("Their public key has %ld bits\n", ECC_QueryKey(NULL, 0, theirpubkey, "keyBits", 0));

	// Compute shared secret
	// - find required length
	nbytes = ECC_DHSharedSecret(NULL, 0, ourprikey, theirpubkey, 0);
	printf("ECC_DHSharedSecret returns %ld\n", nbytes);
	if (nbytes < 0) disp_error(nbytes);
	assert(nbytes > 0);

	// - allocate memory
	lpZZ = malloc(nbytes);
	// - do the business
	nbytes = ECC_DHSharedSecret(lpZZ, nbytes, ourprikey, theirpubkey, 0);

	pr_byteshex("ZZ=", lpZZ, nbytes, "\n");
	pr_byteshex("OK=", zzOK, sizeof(zzOK), "\n");
	assert(0 == memcmp(lpZZ, zzOK, nbytes));

	if (ourprikey) free(ourprikey);
	if (theirpubkey) free(theirpubkey);
	if (lpZZ) free(lpZZ);
}

void ecdh_X25519()
{
	long nchars, nbytes;
	char *ourprikey = NULL;
	char *theirpubkey = NULL;
	unsigned char *lpZZ = NULL;

	// Ref: RFC7748 Section 6.1
	// https://tools.ietf.org/html/rfc7748#section-6.1
	/*
	Test vector:
	Alice's private key, a:
	77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a
	Alice's public key, X25519(a, 9):
	8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a
	Bob's private key, b:
	5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb
	Bob's public key, X25519(b, 9):
	de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f
	Their shared secret, K:
	4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742
	*/
	printf("\nECDH SHARED SECRET USING X25519:\n");
	const char *curve = "X25519";
	const char *alicePrivateKeyHex = "77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a";
	const char *alicePublicKeyHex = "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a";
	const char *bobPrivateKeyHex = "5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb";
	const char *bobPublicKeyHex = "de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f";
	// Correct expected result "4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742"
	const unsigned char zzOK[] = {
		0x4A, 0x5D, 0x9D, 0x5B, 0xA4, 0xCE, 0x2D, 0xE1,
		0x72, 0x8E, 0x3B, 0xF4, 0x80, 0x35, 0x0F, 0x25,
		0xE0, 0x7E, 0x21, 0xC9, 0x47, 0xD1, 0x9E, 0x33,
		0x76, 0xF0, 0x9B, 0x3C, 0x1E, 0x16, 0x17, 0x42,
	};

	printf("1. Alice's private + Bob's public key...\n");
	// NB we *must* specify whether X25519 keys are public or private
	nchars = ECC_ReadKeyByCurve(NULL, 0, alicePrivateKeyHex, curve, PKI_ECC_PRIVATE_KEY);
	printf("ECC_ReadKeyByCurve returns %ld (expecting +ve)\n", nchars);
	if (nchars < 0) disp_error(nchars);
	assert(nchars > 0);
	ourprikey = malloc(nchars + 1);
	nchars = ECC_ReadKeyByCurve(ourprikey, nchars, alicePrivateKeyHex, curve, PKI_ECC_PRIVATE_KEY);
	printf("Alice's private key has %ld bits\n", ECC_QueryKey(NULL, 0, ourprikey, "keyBits", 0));

	// Read in their public key to an internal string (NB PUBLIC_KEY flag)
	nchars = ECC_ReadKeyByCurve(NULL, 0, bobPublicKeyHex, curve, PKI_ECC_PUBLIC_KEY);
	printf("ECC_ReadKeyByCurve returns %ld (expecting +ve)\n", nchars);
	if (nchars < 0) disp_error(nchars);
	assert(nchars > 0);
	theirpubkey = malloc(nchars + 1);
	nchars = ECC_ReadKeyByCurve(theirpubkey, nchars, bobPublicKeyHex, curve, PKI_ECC_PUBLIC_KEY);
	printf("Bob's public key has %ld bits\n", ECC_QueryKey(NULL, 0, theirpubkey, "keyBits", 0));

	// Compute shared secret
	// - find required length
	nbytes = ECC_DHSharedSecret(NULL, 0, ourprikey, theirpubkey, 0);
	printf("ECC_DHSharedSecret returns %ld\n", nbytes);
	if (nbytes < 0) disp_error(nbytes);
	assert(nbytes > 0);

	// - allocate memory
	lpZZ = malloc(nbytes);
	// - do the business
	nbytes = ECC_DHSharedSecret(lpZZ, nbytes, ourprikey, theirpubkey, 0);

	pr_byteshex("ZZ=", lpZZ, nbytes, "\n");
	pr_byteshex("OK=", zzOK, sizeof(zzOK), "\n");
	assert(0 == memcmp(lpZZ, zzOK, nbytes));

	// -- clean up
	if (ourprikey) free(ourprikey);
	if (theirpubkey) free(theirpubkey);
	if (lpZZ) free(lpZZ);

	printf("2. Bob's private + Alice's public key...\n");
	nchars = ECC_ReadKeyByCurve(NULL, 0, bobPrivateKeyHex, curve, PKI_ECC_PRIVATE_KEY);
	printf("ECC_ReadKeyByCurve returns %ld (expecting +ve)\n", nchars);
	if (nchars < 0) disp_error(nchars);
	assert(nchars > 0);
	ourprikey = malloc(nchars + 1);
	nchars = ECC_ReadKeyByCurve(ourprikey, nchars, bobPrivateKeyHex, curve, PKI_ECC_PRIVATE_KEY);
	printf("Bob's private key has %ld bits\n", ECC_QueryKey(NULL, 0, ourprikey, "keyBits", 0));

	// Read in their public key to an internal string (NB PUBLIC_KEY flag)
	nchars = ECC_ReadKeyByCurve(NULL, 0, alicePublicKeyHex, curve, PKI_ECC_PUBLIC_KEY);
	printf("ECC_ReadKeyByCurve returns %ld (expecting +ve)\n", nchars);
	if (nchars < 0) disp_error(nchars);
	assert(nchars > 0);
	theirpubkey = malloc(nchars + 1);
	nchars = ECC_ReadKeyByCurve(theirpubkey, nchars, alicePublicKeyHex, curve, PKI_ECC_PUBLIC_KEY);
	printf("Alice's public key has %ld bits\n", ECC_QueryKey(NULL, 0, theirpubkey, "keyBits", 0));

	// Compute shared secret
	// - find required length
	nbytes = ECC_DHSharedSecret(NULL, 0, ourprikey, theirpubkey, 0);
	printf("ECC_DHSharedSecret returns %ld\n", nbytes);
	if (nbytes < 0) disp_error(nbytes);
	assert(nbytes > 0);

	// - allocate memory
	lpZZ = malloc(nbytes);
	// - do the business
	nbytes = ECC_DHSharedSecret(lpZZ, nbytes, ourprikey, theirpubkey, 0);

	pr_byteshex("ZZ=", lpZZ, nbytes, "\n");
	pr_byteshex("OK=", zzOK, sizeof(zzOK), "\n");
	assert(0 == memcmp(lpZZ, zzOK, nbytes));

	// -- clean up
	if (ourprikey) free(ourprikey);
	if (theirpubkey) free(theirpubkey);
	if (lpZZ) free(lpZZ);
}

void cms_makesig_pseudo(void)
{
	long nRet;
	char *privkey;
	long keylen;
	char *outfile;
	char *signedfile;
	char *exbuffer = NULL;
	long nchars, nbytes;
	char *query;
	char digval64[256];
	char sigval64[1024];
	unsigned char sigval[1024];
	long siglen;
	unsigned char bytes[1024];
	const char *prikeyfile = "ann.p8e";
	const char *password = "password";

	printf("\nCMS MAKESIG PSEUDO:\n");

	/* Read in the private key */
	// Not used until later
	keylen = RSA_ReadAnyPrivateKey(NULL, 0, prikeyfile, password, 0);
	if (keylen > 0) {
		privkey = malloc(keylen + 1);
		keylen = RSA_ReadAnyPrivateKey(privkey, keylen, prikeyfile, password, 0);
		assert(keylen > 0);
	}
	else {
		printf("Cannot read private key\n");
		disp_error(keylen);
		assert(keylen > 0);
		return;
	}

	// Make with some signed attributes and "pseudo" placeholder signature value
	outfile = "signedbyann_pseudo.tmp";
	nRet = CMS_MakeSigData(outfile, "excontent.txt", 	/*REQDIN*/
		"ann.cer", 	/*REQDIN*/
		"", PKI_CMS_PSEUDOSIG /* privkey not required with PSEUDO option */
		| PKI_SIG_RSA_SHA256 | PKI_CMS_ALT_ALGID | PKI_CMS_INCLUDE_ATTRS | PKI_CMS_ADD_SIGNTIME | PKI_CMS_ADD_SIGNINGCERT);
	printf("CMS_MakeSigData(PKI_CMS_PSEUDOSIG) returns %ld\n", nRet);
	if (nRet != 0) disp_error(nRet);
	assert(nRet == 0);
	printf("Created '%s'\n", outfile);
	// Check we can read
	nRet = CMS_ReadSigDataToString(NULL, 0, outfile, 0);
	printf("CMS_ReadSigDataToString(%s) returns %ld (expected len(excontent)=28)\n", outfile, nRet);
	assert(28 == nRet);

	// Query the CMS signedData file
	query = "signingTime";
	nchars = CMS_QuerySigData(NULL, 0, outfile, query, 0);
	assert(nchars > 0);
	exbuffer = malloc(nchars + 1);
	nchars = CMS_QuerySigData(exbuffer, nchars, outfile, query, 0);
	printf("CMS_QuerySigData(%s)=%s\n", query, exbuffer);
	free(exbuffer);

	query = "signingCertHash";
	nchars = CMS_QuerySigData(NULL, 0, outfile, query, 0);
	assert(nchars > 0);
	exbuffer = malloc(nchars + 1);
	nchars = CMS_QuerySigData(exbuffer, nchars, outfile, query, 0);
	printf("CMS_QuerySigData(%s)=%s\n", query, exbuffer);
	free(exbuffer);

	query = "DigestOfSignedAttrs";
	nchars = CMS_QuerySigData(NULL, 0, outfile, query, 0);
	assert(nchars > 0);
	exbuffer = malloc(nchars + 1);
	nchars = CMS_QuerySigData(exbuffer, nchars, outfile, query, 0);
	printf("CMS_QuerySigData(%s)=%s\n", query, exbuffer);
	free(exbuffer);

	query = "digestAlgorithm";
	nchars = CMS_QuerySigData(NULL, 0, outfile, query, 0);
	assert(nchars > 0);
	exbuffer = malloc(nchars + 1);
	nchars = CMS_QuerySigData(exbuffer, nchars, outfile, query, 0);
	printf("CMS_QuerySigData(%s)=%s\n", query, exbuffer);
	free(exbuffer);

	query = "signatureAlgorithm";
	nchars = CMS_QuerySigData(NULL, 0, outfile, query, 0);
	assert(nchars > 0);
	exbuffer = malloc(nchars + 1);
	nchars = CMS_QuerySigData(exbuffer, nchars, outfile, query, 0);
	printf("CMS_QuerySigData(%s)=%s\n", query, exbuffer);
	free(exbuffer);

	// Expecting 0xbbbbbb....
	query = "signatureValue";
	nchars = CMS_QuerySigData(NULL, 0, outfile, query, 0);
	assert(nchars > 0);
	exbuffer = malloc(nchars + 1);
	nchars = CMS_QuerySigData(exbuffer, nchars, outfile, query, 0);
	printf("CMS_QuerySigData(%s)=%s\n", query, exbuffer);
	free(exbuffer);

	// Now simulate user passing digest value to signing service and receiving signature value in return
	signedfile = "signedbyann_resigned.p7m";
	// NB assuming here signed attributes are present
	// Extract required digest value in hex
	query = "DigestOfSignedAttrs";
	nchars = CMS_QuerySigData(NULL, 0, outfile, query, 0);
	assert(nchars > 0);
	exbuffer = malloc(nchars + 1);
	nchars = CMS_QuerySigData(exbuffer, nchars, outfile, query, 0);
	// Convert hex to base64 via temp byte array
	nbytes = CNV_BytesFromHexStr(bytes, sizeof(bytes), exbuffer);
	assert(nbytes > 0);
	nchars = CNV_B64StrFromBytes(digval64, sizeof(digval64) - 1, bytes, nbytes);
	assert(nchars > 0);
	printf("(TO SERVER) DigestValue=%s\n", digval64);
	free(exbuffer);

	// Remote server signs digest value using Alice's private key
	// (quick shortcut for digest value back from base64 to bytes!)
	nchars = SIG_SignData(sigval64, sizeof(sigval64) - 1, bytes, nbytes, privkey, "", "sha256WithRSAEncryption", PKI_SIG_USEDIGEST);
	printf("(FROM SERVER) SigInfo=%s\n", sigval64);

	// Replace placeholder signature in pseudo object with correct value
	siglen = CNV_BytesFromB64Str(sigval, sizeof(sigval), sigval64);
	pr_byteshex("SigVal=", sigval, siglen, "\n");
	nRet = CMS_MakeSigDataFromSigValue(signedfile, sigval, siglen, NULL, 0, outfile, PKI_CMS_PSEUDOSIG);
	printf("CMS_MakeSigDataFromSigValue(PKI_CMS_PSEUDOSIG) returns %ld (expecting 0)\n", nRet);
	assert(nRet == 0);
	printf("Created signed file %s\n", signedfile);

	// Validate the new file
	nRet = CMS_VerifySigData(signedfile, "", "", 0);
	printf("CMS_VerifySigData returns %ld (expecting 0)\n", nRet);
	assert(nRet == 0);


	free(privkey);

}

void ecc_brainpool(void)
{
	char *keyfile = "brainpoolP384r1.p8e";
	char *pubkeyfile = "brainpoolP384r1.pub";
	long r;
	char *password = "password";	// CAUTION: do not hardcode passwords!
	char *keybuf = NULL;
	char qbuf[64];
	long nchars;
	char *sigval = NULL;

	printf("\nECC BRAINPOOL CURVES:\n");

	// CREATE AN ECC KEY PAIR USING BRAINPOOL CURVE
	r = ECC_MakeKeys(pubkeyfile, keyfile, "brainpoolP384r1", password, "", 0);
	printf("ECC_MakeKeys returns %ld (expected 0)\n", r);
	if (r != 0) disp_error(r);
	assert(r == 0);
	printf("Created files '%s' and '%s'\n", pubkeyfile, keyfile);

	// QUERY THE KEY FOR INFO
	// READ IN FIRST TO INTERNAL KEY STRING
	nchars = ECC_ReadPrivateKey(NULL, 0, keyfile, password, 0);
	assert(nchars > 0);
	keybuf = malloc(nchars + 1);
	assert(keybuf);
	nchars = ECC_ReadPrivateKey(keybuf, nchars, keyfile, password, 0);
	// QUERY THE INTERNAL KEY STRING FOR INFO
	r = ECC_QueryKey(NULL, 0, keybuf, "keyBits", 0);
	printf("keyBits: %ld\n", r);
	r = ECC_QueryKey(qbuf, sizeof(qbuf) - 1, keybuf, "curveName", 0);
	printf("curveName: %s\n", qbuf);
	// We are done with the internal key string
	free(keybuf);

	// SIGN SOME DATA
	nchars = SIG_SignData(NULL, 0, (unsigned char*)"abc", 3, keyfile, password, "ecdsaWithSHA384", PKI_SIG_DETERMINISTIC);
	printf("SIG_SignData returns %ld\n", nchars);
	if (nchars < 0) disp_error(nchars);
	sigval = malloc(nchars + 1);
	nchars = SIG_SignData(sigval, nchars, (unsigned char*)"abc", 3, keyfile, password, "ecdsaWithSHA384", PKI_SIG_DETERMINISTIC);
	printf("SIG=%s\n", sigval);

	// Verify the signature
	r = SIG_VerifyData(sigval, (unsigned char*)"abc", 3, pubkeyfile, "ecdsaWithSHA384", 0);
	printf("SIG_VerifyData returns %ld (0 => valid signature)\n", r);

	free(sigval);

}

void test_KDF_Hkdf()
{
	char *testfn = "test_KDF_Hkdf()";
	unsigned char okm[128] = { 0 };
	char *okhex;

	// RFC 5869 Extract-and-Expand HKDF May 2010

	unsigned char ikm_1[] = { // (22 octets)
		0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
		0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
		0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
	};
	unsigned char ikm_2[] = { // (80 octets)
		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
		0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
		0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
		0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
		0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
		0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
		0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
		0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
		0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
	};
	unsigned char ikm_4[] = { // (11 octets)
		0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
		0x0B, 0x0B, 0x0B,
	};
	unsigned char ikm_7[] = { // (22 octets)
		0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
		0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
		0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
	};
	unsigned char salt_1[] = { // (13 octets)
		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
		0x08, 0x09, 0x0A, 0x0B, 0x0C,
	};
	unsigned char salt_2[] = { // (80 octets)
		0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
		0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
		0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
		0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
		0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
		0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
		0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
		0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
		0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
		0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
	};
	unsigned char info_1[] = { // (10 octets)
		0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
		0xF8, 0xF9,
	};
	unsigned char info_2[] = { // (80 octets)
		0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7,
		0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
		0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
		0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
		0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7,
		0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
		0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
		0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
		0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
		0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
	};

	long L;
	long r;

	printf("\nTesting %s...\n", testfn);

	// A.1.  Test Case 1 Basic test case with SHA-256
	printf("Test Case 1\n");
	L = 42;
	okhex = "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865";
	r = KDF_Bytes(okm, L, ikm_1, sizeof(ikm_1), info_1, sizeof(info_1), "salt=000102030405060708090a0b0c", PKI_KDF_HKDF | PKI_HASH_SHA256);
	if (r < 0) disp_error(r);
	assert(0 == r);
	pr_hexencode("OKM=", okm, L, "\n");
	assert(cmp_bytes_with_hex(okm, L, okhex) == 0);

	// A.2.  Test Case 2 Test with SHA-256 and longer inputs/outputs
	printf("Test Case 2\n");
	L = 82;
	okhex = "b11e398dc80327a1c8e7f78c596a49344f012eda2d4efad8a050cc4c19afa97c59045a99cac7827271cb41c65e590e09da3275600c2f09b8367793a9aca3db71cc30c58179ec3e87c14c01d5c1f3434f1d87";
	r = KDF_Bytes(okm, L, ikm_2, sizeof(ikm_2), info_2, sizeof(info_2),
		"salt=606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeaf",
		PKI_KDF_HKDF | PKI_HASH_SHA256);
	if (r < 0) disp_error(r);
	assert(0 == r);
	pr_hexencode("OKM=", okm, L, "\n");
	assert(cmp_bytes_with_hex(okm, L, okhex) == 0);

	// A.3.  Test Case 3 Test with SHA-256 and zero-length salt/info
	printf("Test Case 3\n");
	L = 42;
	okhex = "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d9d201395faa4b61a96c8";
	r = KDF_Bytes(okm, L, ikm_1, sizeof(ikm_1), NULL, 0, "", PKI_KDF_HKDF | PKI_HASH_SHA256);
	if (r < 0) disp_error(r);
	assert(0 == r);
	pr_hexencode("OKM=", okm, L, "\n");
	assert(cmp_bytes_with_hex(okm, L, okhex) == 0);

	// A.4.  Test Case 4 Basic test case with SHA-1
	printf("Test Case 4\n");
	L = 42;
	okhex = "085a01ea1b10f36933068b56efa5ad81a4f14b822f5b091568a9cdd4f155fda2c22e422478d305f3f896";
	r = KDF_Bytes(okm, L, ikm_4, sizeof(ikm_4), info_1, sizeof(info_1), "salt=000102030405060708090a0b0c", PKI_KDF_HKDF | PKI_HASH_SHA1);
	if (r < 0) disp_error(r);
	assert(0 == r);
	pr_hexencode("OKM=", okm, L, "\n");
	assert(cmp_bytes_with_hex(okm, L, okhex) == 0);

	// A.5.  Test Case 5 Test with SHA-1 and longer inputs/outputs
	printf("Test Case 5\n");
	L = 82;
	okhex = "0bd770a74d1160f7c9f12cd5912a06ebff6adcae899d92191fe4305673ba2ffe8fa3f1a4e5ad79f3f334b3b202b2173c486ea37ce3d397ed034c7f9dfeb15c5e927336d0441f4c4300e2cff0d0900b52d3b4";
	r = KDF_Bytes(okm, L, ikm_2, sizeof(ikm_2), info_2, sizeof(info_2),
		"salt=606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeaf",
		PKI_KDF_HKDF | PKI_HASH_SHA1);
	if (r < 0) disp_error(r);
	assert(0 == r);
	pr_hexencode("OKM=", okm, L, "\n");
	assert(cmp_bytes_with_hex(okm, L, okhex) == 0);

	// A.6.  Test Case 6 Test with SHA-1 and zero-length salt/info
	printf("Test Case 6\n");
	L = 42;
	okhex = "0ac1af7002b3d761d1e55298da9d0506b9ae52057220a306e07b6b87e8df21d0ea00033de03984d34918";
	r = KDF_Bytes(okm, L, ikm_1, sizeof(ikm_1), NULL, 0, "", PKI_KDF_HKDF | PKI_HASH_SHA1);
	if (r < 0) disp_error(r);
	assert(0 == r);
	pr_hexencode("OKM=", okm, L, "\n");
	assert(cmp_bytes_with_hex(okm, L, okhex) == 0);

	// A.7.  Test Case 7 Test with SHA-1, salt not provided (defaults to HashLen zero octets), zero-length info
	printf("Test Case 7\n");
	L = 42;
	okhex = "2c91117204d745f3500d636a62f64f0ab3bae548aa53d423b0d1f27ebba6f5e5673a081d70cce7acfc48";
	r = KDF_Bytes(okm, L, ikm_7, sizeof(ikm_7), NULL, 0, "", PKI_KDF_HKDF | PKI_HASH_SHA1);
	if (r < 0) disp_error(r);
	assert(0 == r);
	pr_hexencode("OKM=", okm, L, "\n");
	assert(cmp_bytes_with_hex(okm, L, okhex) == 0);

	printf("...%s tested OK\n", testfn);

}

void test_KDF_X963()
{
	char *testfn = "test_KDF_X963()";
	unsigned char okm[256] = { 0 };
	/*
	ansx963_2001.rsp
	# CAVS 12.0
	# 'ANS X9.63-2001' information for sample
	# SHA sizes tested: SHA-1, SHA-224, SHA-256, SHA-384, SHA-512
	# Generated on Mon Mar 19 14:16:13 2012
	*/

	unsigned char z_1[] = { // (192 bits)
		0x1C, 0x7D, 0x7B, 0x5F, 0x05, 0x97, 0xB0, 0x3D,
		0x06, 0xA0, 0x18, 0x46, 0x6E, 0xD1, 0xA9, 0x3E,
		0x30, 0xED, 0x4B, 0x04, 0xDC, 0x64, 0xCC, 0xDD,
	};
	unsigned char z_2[] = {
		0xFD, 0x17, 0x19, 0x8B, 0x89, 0xAB, 0x39, 0xC4,
		0xAB, 0x5D, 0x7C, 0xCA, 0x36, 0x3B, 0x82, 0xF9,
		0xFD, 0x7E, 0x23, 0xC3, 0x98, 0x4D, 0xC8, 0xA2,
	};
	unsigned char info_2[] = {
		0x85, 0x6A, 0x53, 0xF3, 0xE3, 0x6A, 0x26, 0xBB,
		0xC5, 0x79, 0x28, 0x79, 0xF3, 0x07, 0xCC, 0xE2,
	};
	unsigned char z_3[] = {
		0x22, 0x51, 0x8B, 0x10, 0xE7, 0x0F, 0x2A, 0x3F,
		0x24, 0x38, 0x10, 0xAE, 0x32, 0x54, 0x13, 0x9E,
		0xFB, 0xEE, 0x04, 0xAA, 0x57, 0xC7, 0xAF, 0x7D,
	};
	unsigned char info_3[] = {
		0x75, 0xEE, 0xF8, 0x1A, 0xA3, 0x04, 0x1E, 0x33,
		0xB8, 0x09, 0x71, 0x20, 0x3D, 0x2C, 0x0C, 0x52,
	};
	unsigned char z_4[] = {
		0x00, 0xAA, 0x5B, 0xB7, 0x9B, 0x33, 0xE3, 0x89,
		0xFA, 0x58, 0xCE, 0xAD, 0xC0, 0x47, 0x19, 0x7F,
		0x14, 0xE7, 0x37, 0x12, 0xF4, 0x52, 0xCA, 0xA9,
		0xFC, 0x4C, 0x9A, 0xDB, 0x36, 0x93, 0x48, 0xB8,
		0x15, 0x07, 0x39, 0x2F, 0x1A, 0x86, 0xDD, 0xFD,
		0xB7, 0xC4, 0xFF, 0x82, 0x31, 0xC4, 0xBD, 0x0F,
		0x44, 0xE4, 0x4A, 0x1B, 0x55, 0xB1, 0x40, 0x47,
		0x47, 0xA9, 0xE2, 0xE7, 0x53, 0xF5, 0x5E, 0xF0,
		0x5A, 0x2D,
	};
	unsigned char info_4[] = {
		0xE3, 0xB5, 0xB4, 0xC1, 0xB0, 0xD5, 0xCF, 0x1D,
		0x2B, 0x3A, 0x2F, 0x99, 0x37, 0x89, 0x5D, 0x31,
	};
	long L;
	long r;
	char *okhex;
	printf("\nTesting %s...\n", testfn);

	/*
	[SHA-1]
	[shared secret length = 192]
	[SharedInfo length = 0]
	[key data length = 128]
	*/
	printf("Using SHA-1...\n");
	L = 128 / 8;
	okhex = "bf71dffd8f4d99223936beb46fee8ccc";
	r = KDF_Bytes(okm, L, z_1, sizeof(z_1), NULL, 0, "", PKI_KDF_X963 | PKI_HASH_SHA1);
	if (r < 0) disp_error(r);
	assert(r == 0);
	pr_hexencode("OKM=", okm, L, "\n");
	assert(cmp_bytes_with_hex(okm, L, okhex) == 0);

	/*
	[SHA-1]
	[shared secret length = 192]
	[SharedInfo length = 128]
	[key data length = 1024]
	*/
	L = 1024 / 8;
	okhex = "6e5fad865cb4a51c95209b16df0cc490bc2c9064405c5bccd4ee4832a531fbe7f10cb79e2eab6ab1149fbd5a23cfdabc41242269c9df22f628c4424333855b64e95e2d4fb8469c669f17176c07d103376b10b384ec5763d8b8c610409f19aca8eb31f9d85cc61a8d6d4a03d03e5a506b78d6847e93d295ee548c65afedd2efec";
	r = KDF_Bytes(okm, L, z_2, sizeof(z_2), info_2, sizeof(info_2), "", PKI_KDF_X963 | PKI_HASH_SHA1);
	if (r < 0) disp_error(r);
	assert(r == 0);
	pr_hexencode("OKM=", okm, L, "\n");
	assert(cmp_bytes_with_hex(okm, L, okhex) == 0);

	/*
	[SHA-256]
	[shared secret length = 192]
	[SharedInfo length = 128]
	[key data length = 1024]
	*/
	printf("Using SHA-256...\n");
	L = 1024 / 8;
	okhex = "c498af77161cc59f2962b9a713e2b215152d139766ce34a776df11866a69bf2e52a13d9c7c6fc878c50c5ea0bc7b00e0da2447cfd874f6cf92f30d0097111485500c90c3af8b487872d04685d14c8d1dc8d7fa08beb0ce0ababc11f0bd496269142d43525a78e5bc79a17f59676a5706dc54d54d4d1f0bd7e386128ec26afc21";
	r = KDF_Bytes(okm, L, z_3, sizeof(z_3), info_3, sizeof(info_3), "", PKI_KDF_X963 | PKI_HASH_SHA256);
	if (r < 0) disp_error(r);
	assert(r == 0);
	pr_hexencode("OKM=", okm, L, "\n");
	assert(cmp_bytes_with_hex(okm, L, okhex) == 0);

	/*
	[SHA-512]
	[shared secret length = 521]
	[SharedInfo length = 128]
	[key data length = 1024]
	*/
	printf("Using SHA-512...\n");
	L = 1024 / 8;
	okhex = "4463f869f3cc18769b52264b0112b5858f7ad32a5a2d96d8cffabf7fa733633d6e4dd2a599acceb3ea54a6217ce0b50eef4f6b40a5c30250a5a8eeee208002267089dbf351f3f5022aa9638bf1ee419dea9c4ff745a25ac27bda33ca08bd56dd1a59b4106cf2dbbc0ab2aa8e2efa7b17902d34276951ceccab87f9661c3e8816";
	r = KDF_Bytes(okm, L, z_4, sizeof(z_4), info_4, sizeof(info_4), "", PKI_KDF_X963 | PKI_HASH_SHA512);
	if (r < 0) disp_error(r);
	assert(r == 0);
	pr_hexencode("OKM=", okm, L, "\n");
	assert(cmp_bytes_with_hex(okm, L, okhex) == 0);

	printf("...%s tested OK\n", testfn);
}

void test_CMS_MakeEnvData_ECDH(void)
{
	char *testfn = "CMS_MakeEnvData_ECDH()";

	char *szFileOut;
	char *szCertList;
	long knrecips = 1;
	long lResult;
	char *password = "";
	char *query;
	char qrybuf[256];
	long r;

	printf("\nTesting %s...\n", testfn);

	// We hardcode the certificates in PEM format
	//szCertList = "lamps-dana.encrypt.crt;lamps-alice.encrypt.crt";
	szCertList = "-----BEGIN CERTIFICATE-----"
		"MIICMDCCAeKgAwIBAgITDksKNqnvupyaO2gkjlIdwN7zpzAFBgMrZXAwWTENMAsG"
		"A1UEChMESUVURjERMA8GA1UECxMITEFNUFMgV0cxNTAzBgNVBAMTLFNhbXBsZSBM"
		"QU1QUyBFZDI1NTE5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MCAXDTIwMTIxNTIx"
		"MzU0NFoYDzIwNTIxMjE1MjEzNTQ0WjA4MQ0wCwYDVQQKEwRJRVRGMREwDwYDVQQL"
		"EwhMQU1QUyBXRzEUMBIGA1UEAxMLRGFuYSBIb3BwZXIwKjAFBgMrZW4DIQDgMaI2"
		"AWkU9LG8CvaRHgDSEY9d72Y8ENZeMwibPugkVKOB2zCB2DArBgkqhkiG9w0BCQ8E"
		"HjAcMBoGCyqGSIb3DQEJEAMTMAsGCWCGSAFlAwQBBTAMBgNVHRMBAf8EAjAAMBcG"
		"A1UdIAQQMA4wDAYKYIZIAWUDAgEwATAdBgNVHREEFjAUgRJkYW5hQHNtaW1lLmV4"
		"YW1wbGUwEwYDVR0lBAwwCgYIKwYBBQUHAwQwDgYDVR0PAQH/BAQDAgMIMB0GA1Ud"
		"DgQWBBSd303UBe+a7GCGvCdtBOnOWtyPpDAfBgNVHSMEGDAWgBRropV9uhSb5C0E"
		"0Qek0YLkLmuMtTAFBgMrZXADQQD6f7DCCxXzpnY3BwmrIuf/SNQSf//Otri7USkd"
		"9GF+VthGS+9KJ4HTBCh0ZGuHIU9EgnfgdSL1UR3WUkL7tv8A"
		"-----END CERTIFICATE-----"
		";"
		"-----BEGIN CERTIFICATE-----"
		"MIIDzzCCAregAwIBAgITDy0lvRE5l0rOQlSHoe49NAaKtDANBgkqhkiG9w0BAQ0F"
		"ADBVMQ0wCwYDVQQKEwRJRVRGMREwDwYDVQQLEwhMQU1QUyBXRzExMC8GA1UEAxMo"
		"U2FtcGxlIExBTVBTIFJTQSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAgFw0xOTEx"
		"MjAwNjU0MThaGA8yMDUyMDkyNzA2NTQxOFowOzENMAsGA1UEChMESUVURjERMA8G"
		"A1UECxMITEFNUFMgV0cxFzAVBgNVBAMTDkFsaWNlIExvdmVsYWNlMIIBIjANBgkq"
		"hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmpUp+ovBouOP6AFQJ+RpwpODxxzY60n1"
		"lJ53pTeNSiJlWkwtw/cxQq0t4uD2vWYB8gOUH/CVt2Zp1c+auzPKJ2Zu5mY6kHm+"
		"hVB+IthjLeI7Htg6rNeuXq50/TuTSxX5R1I1EXGt8p6hAQVeA5oZ2afHg4b97enV"
		"8gozR0/Nkug4AkXmbk7THNc8vvjMUJanZ/VmS4TgDqXjWShplcI3lcvvBZMswt41"
		"/0HJvmSwqpS6oQcAx3Weag0yCNj1V9V9yu/3DjcYbwW2lJf5NbMHbM1LY4X5chWf"
		"NEbkN6hQury/zxnlsukgn+fHbqvwDhJLAgFpW/jA/EB/WI+whUpqtQIDAQABo4Gv"
		"MIGsMAwGA1UdEwEB/wQCMAAwFwYDVR0gBBAwDjAMBgpghkgBZQMCATABMB4GA1Ud"
		"EQQXMBWBE2FsaWNlQHNtaW1lLmV4YW1wbGUwEwYDVR0lBAwwCgYIKwYBBQUHAwQw"
		"DgYDVR0PAQH/BAQDAgUgMB0GA1UdDgQWBBSiU0HVRDyAKRV8ASPw546vzfN3DzAf"
		"BgNVHSMEGDAWgBSRMI58BxcMp/EJKGU2GmccaHb0WTANBgkqhkiG9w0BAQ0FAAOC"
		"AQEAgUl4oJyxMpwWpAylOvK6NEbMl1gD5H14EC4Muxq1u0q2XgXOSBHI6DfX/4LD"
		"sfx7fSIus8gWVY3WqMeuOA7IizkBD+GDEu8uKveERRXZncxGwy2MfbH1Ib3U8QzT"
		"jqB8+dz2AwYeMxODWq9opwtA/lTOkRg8uuivZfg/m5fFo/QshlHNaaTDVEXsU4Ps"
		"98Hm/3gznbvhdjFbZbi4oZ3tAadRlE5K9JiQaJYOnUmGpfB8PPwDR6chMZeegSQA"
		"W++OIKqHrg/WEh4yiuPfqmAvX2hZkPpivNJYdTPUXTSO7K459CyqbqG+sNOo2kc1"
		"nTXl85RHNrVKQK+L0YWY1Q+hWA=="
		"-----END CERTIFICATE-----";

	// Make EnvData for 2 recipients, one (dana) with an X25519 EC key, the other (alice) with an RSA key.
	knrecips = 2;
	szFileOut = "ecdh-dana-alice.p7m";
	lResult = CMS_MakeEnvDataFromString(szFileOut, "This is some sample data", szCertList, "#xdeadbeef01", 0, PKI_KDF_HKDF | PKI_KWRAP_AES192 | PKI_BC_AES128 | PKI_HASH_SHA256 | PKI_KT_RSAES_OAEP | PKI_MGF_MGF1SHA1);
	// Returns # successful recipients in enveloped data or -ve code if error
	printf("CMS_MakeEnvDataFromString() returns %ld (expecting %ld recips)\n", lResult, knrecips);
	if (lResult > 0) {
		printf("Created file '%s'\n", szFileOut);
		if (PKI_LastError(NULL, 0) > 0)
			disp_error(0);
	}
	else {
		disp_error(lResult);
	}
	assert(lResult == knrecips);

	query = "contentEncryptionAlgorithm";
	r = CMS_QueryEnvData(qrybuf, sizeof(qrybuf) - 1, szFileOut, query, 0);
	printf("%s=%s\n", query, qrybuf);
	query = "recipientInfoType";
	r = CMS_QueryEnvData(qrybuf, sizeof(qrybuf) - 1, szFileOut, query, 0);
	printf("%s=%s\n", query, qrybuf);
	query = "recipientInfoType/2";
	r = CMS_QueryEnvData(qrybuf, sizeof(qrybuf) - 1, szFileOut, query, 0);
	printf("%s=%s\n", query, qrybuf);
	query = "keyEncryptionAlgorithm";
	r = CMS_QueryEnvData(qrybuf, sizeof(qrybuf) - 1, szFileOut, query, 0);
	printf("%s=%s\n", query, qrybuf);
	query = "keyWrapAlgorithm";
	r = CMS_QueryEnvData(qrybuf, sizeof(qrybuf) - 1, szFileOut, query, 0);
	printf("%s=%s\n", query, qrybuf);
	query = "keyEncryptionAlgorithm/2";
	r = CMS_QueryEnvData(qrybuf, sizeof(qrybuf) - 1, szFileOut, query, 0);
	printf("%s=%s\n", query, qrybuf);
	query = "oaepParams/2";
	r = CMS_QueryEnvData(qrybuf, sizeof(qrybuf) - 1, szFileOut, query, 0);
	printf("%s=%s\n", query, qrybuf);

}

void test_CMS_MakeEnvData_adv(void)
{
	char *testfn = "CMS_MakeEnvData_examples()";
	const char *bob_cert =	/* lamps-bob.crt */
		"-----BEGIN CERTIFICATE-----"
		"MIIDyjCCArKgAwIBAgITaqOkD33fBy/kGaVsmPv8LghbwzANBgkqhkiG9w0BAQ0F"
		"ADBVMQ0wCwYDVQQKEwRJRVRGMREwDwYDVQQLEwhMQU1QUyBXRzExMC8GA1UEAxMo"
		"U2FtcGxlIExBTVBTIFJTQSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAgFw0xOTEx"
		"MjAwNjU0MThaGA8yMDUyMDkyNzA2NTQxOFowODENMAsGA1UEChMESUVURjERMA8G"
		"A1UECxMITEFNUFMgV0cxFDASBgNVBAMTC0JvYiBCYWJiYWdlMIIBIjANBgkqhkiG"
		"9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5nAF0glRof9NjBKke6g+7RLrOgRfwQjcH+2z"
		"m0Af67FJRNrEwTuOutlWamUA3p9+wb7XqizVHOQhVesjwgp8PJpo8Adm8ar84d2t"
		"tey1OVdxaCJuNe7SJjfrwShB6NvAm7S8CDG3+EapkO9fzn2pWwaREQ6twWtHi1QT"
		"51PduRtiQ1oqsuJk8LBDgUMZlKUsaXfF8GKzJlGuaLRl5/3Kfr9+b6VkCDuxTZYL"
		"Zxt6+a3/QkaC3I9m2ygPubtHFJB5P5+s8boROSKm1OB1gsLow8eF9S7OtcGGeooZ"
		"JiJUQCR14NaU5bIyfKEZV2YStXwdztoEJJ2fRURIK+8YnwlB3QIDAQABo4GtMIGq"
		"MAwGA1UdEwEB/wQCMAAwFwYDVR0gBBAwDjAMBgpghkgBZQMCATABMBwGA1UdEQQV"
		"MBOBEWJvYkBzbWltZS5leGFtcGxlMBMGA1UdJQQMMAoGCCsGAQUFBwMEMA4GA1Ud"
		"DwEB/wQEAwIGwDAdBgNVHQ4EFgQUF8WEe9Cn73aQOLizbwi8krWeK5QwHwYDVR0j"
		"BBgwFoAUkTCOfAcXDKfxCShlNhpnHGh29FkwDQYJKoZIhvcNAQENBQADggEBAG7e"
		"QY6Px7WZC5vCbF5hjOitxoz3oyM+LRcSTGWoYXdmlwsNUzy31pE3dtADvevRtsP8"
		"uN7xyfK6XZBzhShA/BtkkqYGiFvXDpluOxWmqC0WPmc1PNK2mHil+pGMfvnUwnxd"
		"6gKcHED5p+bUhDyIH2fy9hGyeOUs8nvi+7/HwBipN+nA/PfsPn+aU4l1K6qDoG/i"
		"kwyuiWcFFlc5yE5rkAe2J0/a4+HtzNmTK4jB/4GbyI6xlUszPlEqKE+Es10Xut/y"
		"UWL5nKKaqpRRd07Pq371MpFQs2+zXt4fGheKzZU3XXrIPcAPyJjWiyU1DzpqgSJM"
		"OIp/HtXdFscHb9+Qic8="
		"-----END CERTIFICATE-----";
	const char *dana_encrypt_cert = /* lamps-dana.encrypt.crt */
		"-----BEGIN CERTIFICATE-----"
		"MIICMDCCAeKgAwIBAgITDksKNqnvupyaO2gkjlIdwN7zpzAFBgMrZXAwWTENMAsG"
		"A1UEChMESUVURjERMA8GA1UECxMITEFNUFMgV0cxNTAzBgNVBAMTLFNhbXBsZSBM"
		"QU1QUyBFZDI1NTE5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MCAXDTIwMTIxNTIx"
		"MzU0NFoYDzIwNTIxMjE1MjEzNTQ0WjA4MQ0wCwYDVQQKEwRJRVRGMREwDwYDVQQL"
		"EwhMQU1QUyBXRzEUMBIGA1UEAxMLRGFuYSBIb3BwZXIwKjAFBgMrZW4DIQDgMaI2"
		"AWkU9LG8CvaRHgDSEY9d72Y8ENZeMwibPugkVKOB2zCB2DArBgkqhkiG9w0BCQ8E"
		"HjAcMBoGCyqGSIb3DQEJEAMTMAsGCWCGSAFlAwQBBTAMBgNVHRMBAf8EAjAAMBcG"
		"A1UdIAQQMA4wDAYKYIZIAWUDAgEwATAdBgNVHREEFjAUgRJkYW5hQHNtaW1lLmV4"
		"YW1wbGUwEwYDVR0lBAwwCgYIKwYBBQUHAwQwDgYDVR0PAQH/BAQDAgMIMB0GA1Ud"
		"DgQWBBSd303UBe+a7GCGvCdtBOnOWtyPpDAfBgNVHSMEGDAWgBRropV9uhSb5C0E"
		"0Qek0YLkLmuMtTAFBgMrZXADQQD6f7DCCxXzpnY3BwmrIuf/SNQSf//Otri7USkd"
		"9GF+VthGS+9KJ4HTBCh0ZGuHIU9EgnfgdSL1UR3WUkL7tv8A"
		"-----END CERTIFICATE-----";

	char *query;
	char qrybuf[256];
	char *fname;
	long n, r;

	printf("\nTesting %s...\n", testfn);

	// Create an enveloped CMS object (ktri type) to Bob using Bob's RSA key...
	n = CMS_MakeEnvData("cms2bob_aes128.p7m", "excontent.txt", bob_cert, "", 0, PKI_BC_AES128 | PKI_KT_RSAES_OAEP);
	printf("CMS_MakeEnvData returns %ld (expecting 1)\n", n);
	if (n != 1) disp_error(n);
	assert(1 == n);
	fname = "cms2bob_aes128.p7m";
	printf("FILE: %s\n", fname);
	query = "recipientInfoType";
	r = CMS_QueryEnvData(qrybuf, sizeof(qrybuf) - 1, fname, query, 0);
	printf("%s=%s\n", query, qrybuf);
	query = "contentEncryptionAlgorithm";
	r = CMS_QueryEnvData(qrybuf, sizeof(qrybuf) - 1, fname, query, 0);
	printf("%s=%s\n", query, qrybuf);
	r = ASN1_Type(qrybuf, sizeof(qrybuf) - 1, fname, 0);
	printf("ASN1_Type=%s\n", qrybuf);

	// Same but using authenticated encryption and creating an authEnvelopedData object...
	n = CMS_MakeEnvData("cms2bob_aes128auth.p7m", "excontent.txt", bob_cert, "", 0, PKI_AEAD_AES_128_GCM | PKI_KT_RSAES_OAEP);
	printf("CMS_MakeEnvData returns %ld (expecting 1)\n", n);
	if (n != 1) disp_error(n);
	assert(1 == n);
	fname = "cms2bob_aes128auth.p7m";
	printf("FILE: %s\n", fname);
	query = "recipientInfoType";
	r = CMS_QueryEnvData(qrybuf, sizeof(qrybuf) - 1, fname, query, 0);
	printf("%s=%s\n", query, qrybuf);
	query = "contentEncryptionAlgorithm";
	r = CMS_QueryEnvData(qrybuf, sizeof(qrybuf) - 1, fname, query, 0);
	printf("%s=%s\n", query, qrybuf);
	r = ASN1_Type(qrybuf, sizeof(qrybuf) - 1, fname, 0);
	printf("ASN1_Type=%s\n", qrybuf);

	// Create an enveloped CMS object (kari type) to Dana using Dana's ECC key...
	n = CMS_MakeEnvData("cms2dana_hkdf.p7m", "excontent.txt", dana_encrypt_cert, "", 0, PKI_BC_AES256 | PKI_HASH_SHA256 | PKI_KDF_HKDF | PKI_KWRAP_AES256);
	printf("CMS_MakeEnvData returns %ld (expecting 1)\n", n);
	if (n != 1) disp_error(n);
	assert(1 == n);
	fname = "cms2dana_hkdf.p7m";
	printf("FILE: %s\n", fname);
	query = "recipientInfoType";
	r = CMS_QueryEnvData(qrybuf, sizeof(qrybuf) - 1, fname, query, 0);
	printf("%s=%s\n", query, qrybuf);
	query = "keyWrapAlgorithm";
	r = CMS_QueryEnvData(qrybuf, sizeof(qrybuf) - 1, fname, query, 0);
	printf("%s=%s\n", query, qrybuf);

	// Create an enveloped CMS object (kekri type) using a previously distributed symmetric key-encryption key (KEK)...
	n = CMS_MakeEnvData("cms_envdata_kekri.p7m", "excontent.txt", "type=@kekri,keyid=ourcommonkey", "#x0123456789ABCDEFF0E1D2C3B4A59687", 0, PKI_BC_AES256 | PKI_HASH_SHA256 | PKI_KWRAP_AES128);
	printf("CMS_MakeEnvData returns %ld (expecting 1)\n", n);
	if (n != 1) disp_error(n);
	assert(1 == n);
	fname = "cms_envdata_kekri.p7m";
	printf("FILE: %s\n", fname);
	query = "recipientInfoType";
	r = CMS_QueryEnvData(qrybuf, sizeof(qrybuf) - 1, fname, query, 0);
	printf("%s=%s\n", query, qrybuf);
	query = "keyid";
	r = CMS_QueryEnvData(qrybuf, sizeof(qrybuf) - 1, fname, query, 0);
	printf("%s=%s\n", query, qrybuf);
	query = "keyWrapAlgorithm";
	r = CMS_QueryEnvData(qrybuf, sizeof(qrybuf) - 1, fname, query, 0);
	printf("%s=%s\n", query, qrybuf);

	// Create an enveloped CMS object (pwri type) using password-based key management...
	n = CMS_MakeEnvData("cms_envdata_pwri.p7m", "excontent.txt", "type=@pwri", "password12345", 0, PKI_BC_AES192);
	printf("CMS_MakeEnvData returns %ld (expecting 1)\n", n);
	if (n != 1) disp_error(n);
	assert(1 == n);
	fname = "cms_envdata_pwri.p7m";
	printf("FILE: %s\n", fname);
	query = "recipientInfoType";
	r = CMS_QueryEnvData(qrybuf, sizeof(qrybuf) - 1, fname, query, 0);
	printf("%s=%s\n", query, qrybuf);
}

void test_X509_MakeCert_InternalKeys_25519_encrypt(void)
{
	char *testfn = "test_X509_MakeCert_InternalKeys_25519_encrypt()";

	char *certname, *issuercert;
	char *dn, *extns;
	long keyUsage;
	char *prikeyfile;
	char *pubkeyfile;
	char *prikeystr;
	char *pubkeystr;
	long nRet, nchars;


	printf("\nTesting %s...\n", testfn);

	//  Create a new certificate using internal key strings
	certname = "new-lamps-dana.encrypt.cer";
	dn = "O=IETF;OU=LAMPS WG;CN=Dana Hopper";
	extns = "serialNumber=#x0E4B0A36A9EFBA9C9A3B68248E521DC0DEF3A7;notBefore=2020-12-15T21:35:44;notAfter=2052-12-15T21:35:44;extKeyUsage=emailProtection;"
		"keyUsage=keyAgreement;sMIMECapabilities=300B0609608648016503040102,301A060B2A864886F70D0109100313300B0609608648016503040105;"
		"certificatePolicies=2.16.840.1.101.3.2.1.48.1;rfc822Name=dana@smime.example;subjectKeyIdentifier=9ddf4dd405ef9aec6086bc276d04e9ce5adc8fa4";
	keyUsage = 0;

	// We pass the required certs and keys using PEM format strings
	//issuercert = "lamps-ca.ed25519.crt";
	//prikeyfile = "lamps-ca.ed25519.p8";	// No password
	//pubkeyfile = "lamps-dana.encrypt.crt";
	issuercert = "-----BEGIN CERTIFICATE-----"
		"MIIBtzCCAWmgAwIBAgITH59R65FuWGNFHoyc0N3iWesrXzAFBgMrZXAwWTENMAsG"
		"A1UEChMESUVURjERMA8GA1UECxMITEFNUFMgV0cxNTAzBgNVBAMTLFNhbXBsZSBM"
		"QU1QUyBFZDI1NTE5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MCAXDTIwMTIxNTIx"
		"MzU0NFoYDzIwNTIxMjE1MjEzNTQ0WjBZMQ0wCwYDVQQKEwRJRVRGMREwDwYDVQQL"
		"EwhMQU1QUyBXRzE1MDMGA1UEAxMsU2FtcGxlIExBTVBTIEVkMjU1MTkgQ2VydGlm"
		"aWNhdGlvbiBBdXRob3JpdHkwKjAFBgMrZXADIQCEgUZ9yI/rkX/82DihqzVIZQZ+"
		"RKE3URyp+eN2TxJDBKNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC"
		"AQYwHQYDVR0OBBYEFGuilX26FJvkLQTRB6TRguQua4y1MAUGAytlcANBAFAJrlWo"
		"QjzwT0ph7rXe023x3GaLPMXMwQI2Of+apkdG2mH9ID6PE1bu3gRRqIH5w2tyS+xF"
		"Jw0ouxcJyAyXEQ4="
		"-----END CERTIFICATE-----";
	prikeyfile = "-----BEGIN PRIVATE KEY-----"
		"MC4CAQAwBQYDK2VwBCIEIAt889xRDvxNT8ak53T7tzKuSn6CQDe8fIdjrCiSFRcp"
		"-----END PRIVATE KEY-----";
	pubkeyfile = "-----BEGIN CERTIFICATE-----"
		"MIICMDCCAeKgAwIBAgITDksKNqnvupyaO2gkjlIdwN7zpzAFBgMrZXAwWTENMAsG"
		"A1UEChMESUVURjERMA8GA1UECxMITEFNUFMgV0cxNTAzBgNVBAMTLFNhbXBsZSBM"
		"QU1QUyBFZDI1NTE5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MCAXDTIwMTIxNTIx"
		"MzU0NFoYDzIwNTIxMjE1MjEzNTQ0WjA4MQ0wCwYDVQQKEwRJRVRGMREwDwYDVQQL"
		"EwhMQU1QUyBXRzEUMBIGA1UEAxMLRGFuYSBIb3BwZXIwKjAFBgMrZW4DIQDgMaI2"
		"AWkU9LG8CvaRHgDSEY9d72Y8ENZeMwibPugkVKOB2zCB2DArBgkqhkiG9w0BCQ8E"
		"HjAcMBoGCyqGSIb3DQEJEAMTMAsGCWCGSAFlAwQBBTAMBgNVHRMBAf8EAjAAMBcG"
		"A1UdIAQQMA4wDAYKYIZIAWUDAgEwATAdBgNVHREEFjAUgRJkYW5hQHNtaW1lLmV4"
		"YW1wbGUwEwYDVR0lBAwwCgYIKwYBBQUHAwQwDgYDVR0PAQH/BAQDAgMIMB0GA1Ud"
		"DgQWBBSd303UBe+a7GCGvCdtBOnOWtyPpDAfBgNVHSMEGDAWgBRropV9uhSb5C0E"
		"0Qek0YLkLmuMtTAFBgMrZXADQQD6f7DCCxXzpnY3BwmrIuf/SNQSf//Otri7USkd"
		"9GF+VthGS+9KJ4HTBCh0ZGuHIU9EgnfgdSL1UR3WUkL7tv8A"
		"-----END CERTIFICATE-----";
	// Read in private and public keys to internal key strings
	nchars = ECC_ReadPrivateKey(NULL, 0, prikeyfile, "", 0);
	assert(nchars > 0);
	prikeystr = malloc(nchars + 1);
	nchars = ECC_ReadPrivateKey(prikeystr, nchars, prikeyfile, "", 0);
	printf("PRIKEY %ld bits 0x%08X\n", ECC_QueryKey(NULL, 0, prikeystr, "keyBits", 0), ECC_KeyHashCode(prikeystr));

	nchars = ECC_ReadPublicKey(NULL, 0, pubkeyfile, 0);
	assert(nchars > 0);
	pubkeystr = malloc(nchars + 1);
	nchars = ECC_ReadPublicKey(pubkeystr, nchars, pubkeyfile, 0);
	printf("PUBKEY %ld bits 0x%08X\n", ECC_QueryKey(NULL, 0, pubkeystr, "keyBits", 0), ECC_KeyHashCode(pubkeystr));

	// Pass issuer's private key and subject's public key as internal key strings.
	nRet = X509_MakeCert(certname, issuercert, pubkeystr, prikeystr,
		0x0ACE256, 0, dn, extns, keyUsage, "", PKI_SIG_ED25519 | PKI_X509_AUTHKEYID);
	printf("X509_MakeCert returns %ld (expecting 0)\n", nRet);
	if (nRet != 0) disp_error(nRet);
	assert(nRet == 0);
	printf("Created X.509 certificate '%s'\n", certname);

	free(prikeystr);
	free(pubkeystr);

	x509DumpFile(certname, 0);

}

void test_CNV_ShortPathName()
{
	char *testfn = "test_CNV_ShortPathName()";

	#include <wchar.h>
	long nchars;
	wchar_t *wfname;
	char *shortname;

	printf("\nTesting %s...\n", testfn);

	wfname = L"你好.txt";
	// Find required output length
	nchars = CNV_ShortPathName(NULL, 0, wfname);
	printf("CNV_ShortPathName returns %ld\n", nchars);
	assert(nchars > 0);
	shortname = malloc(nchars + 1);
	nchars = CNV_ShortPathName(shortname, nchars, wfname);
	assert(nchars > 0);
	// Output should be in ASCII
	printf("ShortPath='%s'\n", shortname);
	// ShortPath='FC0F~1.TXT'
	free(shortname);
}

void test_CIPHER_Encrypt_GCM()
{
	char *testfn = "test_CIPHER_Encrypt_GCM()";

	unsigned char key128[] = {
		0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6,
		0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C,
	};
	unsigned char iv[] = {
		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
		0x08, 0x09, 0x0A, 0x0B,
	};

	long r;
	char *filein, *fileenc, *filechk;

	printf("\nTesting %s...\n", testfn);

	filein = "abc.txt";
	fileenc = "abc-gcm-enc.dat";
	filechk = "abc-gcm-chk.dat";

	// Encrypt file using AES128-GCM, prepending 12-byte IV to output file with 16-byte tag at end
	// IV(12) || ciphertext || tag(16)
	printf("About to encrypt file '%s' length=%ld\n", filein, file_length(filein));
	r = CIPHER_FileEncrypt(fileenc, filein, key128, sizeof(key128), iv, sizeof(iv), "aes128-gcm", PKI_IV_PREFIX);
	printf("CIPHER_FileEncrypt(GCM) returns %ld (expected 0)\n", r);
	if (r != 0) disp_error(r);
	assert(0 == r);
	printf("Created file '%s' length=%ld\n", fileenc, file_length(fileenc));

	// Decrypt - IV is already at start of file
	printf("About to decrypt file '%s'\n", fileenc);
	r = CIPHER_FileDecrypt(filechk, fileenc, key128, sizeof(key128), NULL, 0, "aes128-gcm", PKI_IV_PREFIX);
	printf("CIPHER_FileDecrypt(GCM) returns %ld (expected 0)\n", r);
	if (r != 0) disp_error(r);
	assert(0 == r);
	printf("Created file '%s' length=%ld\n", filechk, file_length(filechk));
	assert(0 == cmp_files(filechk, filein));

}

void test_RSA_ReadPublicKey_CSR()
{
	char *testfn = "test_RSA_ReadPublicKey_CSR()";

	char *csrfile;
	char *keystr = NULL;
	char *xmlkey = NULL;
	long nchars;

	printf("\nTesting %s...\n", testfn);

	// Read in public key from CSR file we made earlier as an internal key string
	csrfile = "damien_csr.p10";
	nchars = RSA_ReadAnyPublicKey(NULL, 0, csrfile, 0);
	printf("RSA_ReadAnyPublicKey returns %ld (expected +ve)\n", nchars);
	assert(nchars > 0);
	keystr = malloc(nchars + 1);
	nchars = RSA_ReadAnyPublicKey(keystr, nchars, csrfile, 0);
	assert(nchars > 0);
	printf("Keysize=%ld bits, HashCode=0x%08X\n", RSA_KeyBits(keystr), RSA_KeyHashCode(keystr));
	
	// For fun, print out in XML format
	nchars = RSA_ToXMLString(NULL, 0, keystr, PKI_XML_HEXBINARY);
	assert(nchars > 0);
	xmlkey = malloc(nchars + 1);
	nchars = RSA_ToXMLString(xmlkey, nchars, keystr, PKI_XML_HEXBINARY);
	printf("%s\n", xmlkey);

	free(xmlkey);
	free(keystr);
}

void test_X509_MakeCert_Ex()
{
	char *testfn = "test_X509_MakeCert_Ex()";
	long r, nchars;
	char *dn, *extns;
	char *certname = "myca-newattributes2022.cer";
	char *keyfile = "carol.p8e";
	char *password = "password";
	int serialnum;
	char *qbuf = NULL;
	char *query;

	printf("\nTesting %s...\n", testfn);

	serialnum = 0x88B;
	dn = "C=AU;dnQualifier='distinguisher';initials='L.C.';pseudonym='CaroMax';generationQualifier='3rd.';CN=Carol";
	extns = "keyUsage=digitalSignature,nonRepudiation;extKeyUsage=emailProtection;rfc822Name=carol@example.com;cRLDistributionPoints=http://dodgycert.example.com/evca.crl;";

	r = X509_MakeCertSelf(certname, keyfile, serialnum, 10, dn, extns, 0, password, PKI_SIG_SHA256RSA);

	printf("X509_MakeCertSelf returns %ld (expected 0)\n", r);
	assert(0 == r);

	// Dump details of cert we just made...
	x509DumpFile(certname, PKI_X509_LDAP);

	// Query the cert
	query = "subjectName";
	nchars = X509_QueryCert(NULL, 0, certname, query, 0);
	assert(nchars > 0);
	qbuf = malloc(nchars + 1);
	nchars = X509_QueryCert(qbuf, nchars, certname, query, 0);
	printf("%s='%s'\n", query, qbuf);
	free(qbuf);
}

void test_FormatErrorMessage()
{
	char *testfn = "test_FormatErrorMessage()";
	printf("\nTesting %s...\n", testfn);
	{
		char szErrMsg[1024];
		long errcode = 11;
		PKI_FormatErrorMessage(szErrMsg, sizeof(szErrMsg) - 1, errcode, "User message!");
		printf("%s\n", szErrMsg);
		// ERROR (11): User message!: Value out of range (OUT_OF_RANGE_ERROR)
	}
}

void test_HASH_HexFromBytes_SHA3(void)
{
	#define OK_SHA3_224_ABC "e642824c3f8cf24ad09234ee7d3c766fc9a3a5168d0c94ad73b46fdf"
	#define OK_SHA3_256_ABC "3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532"
	#define OK_SHA3_384_ABC "ec01498288516fc926459f58e2c6ad8df9b473cb0fc08c2596da7cf0e49be4b298d88cea927ac7f539f1edf228376d25"
	#define OK_SHA3_512_ABC "b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0"
	#define OK_SHA3_256_EMPTY "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a"

	char *testfn = "HASH_HexFromBytes_SHA3()";

	long nRet;
	char szDigest[PKI_MAX_HASH_CHARS + 1]; /* NB extra one for terminating null character */
	char message[] = "abc";

	printf("\nTesting %s...\n", testfn);

	/* Compute SHA-3-224 digest */
	nRet = HASH_HexFromBytes(szDigest, sizeof(szDigest) - 1,
		(unsigned char*)message, (long)strlen(message), PKI_HASH_SHA3_224);
	assert(nRet > 0);
	printf("SHA-3-224('abc')=%s\n", szDigest);
	assert(strcmp(szDigest, OK_SHA3_224_ABC) == 0);

	/* Compute SHA-3-256 digest */
	nRet = HASH_HexFromBytes(szDigest, sizeof(szDigest) - 1,
		(unsigned char*)message, (long)strlen(message), PKI_HASH_SHA3_256);
	assert(nRet > 0);
	printf("SHA-3-256('abc')=%s\n", szDigest);
	assert(strcmp(szDigest, OK_SHA3_256_ABC) == 0);

	/* Compute SHA-3-384 digest */
	nRet = HASH_HexFromBytes(szDigest, sizeof(szDigest) - 1,
		(unsigned char*)message, (long)strlen(message), PKI_HASH_SHA3_384);
	assert(nRet > 0);
	printf("SHA-3-384('abc')=%s\n", szDigest);
	assert(strcmp(szDigest, OK_SHA3_384_ABC) == 0);

	/* Compute SHA-3-512 digest */
	nRet = HASH_HexFromBytes(szDigest, sizeof(szDigest) - 1,
		(unsigned char*)message, (long)strlen(message), PKI_HASH_SHA3_512);
	assert(nRet > 0);
	printf("SHA-3-512('abc')=%s\n", szDigest);
	assert(strcmp(szDigest, OK_SHA3_512_ABC) == 0);

	/* Hash of empty string */
	nRet = HASH_HexFromBytes(szDigest, sizeof(szDigest) - 1,
		(unsigned char*)"", (long)0, PKI_HASH_SHA3_256);
	assert(nRet > 0);
	printf("SHA-3-256('')=%s\n", szDigest);
	assert(strcmp(szDigest, OK_SHA3_256_EMPTY) == 0);

	printf("...%s tested OK\n", testfn);
}

void test_SHA3_HMAC(void)
{
	char *testfn = "test_SHA3_HMAC()";

	long lRet;
	char szDigest[PKI_MAX_HASH_CHARS + 1]; /* NB extra one for terminating null character */
	unsigned char *data;
	unsigned char key1[] = {
		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
		0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
		0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
	};
	unsigned char key3[] = {
		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
		0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
		0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
		0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
		0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
		0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
		0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
		0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
		0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
		0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
		0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
		0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
		0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
		0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
		0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
		0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
		0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
		0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
		0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
		0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
	};
	unsigned char key512_3[] = {
		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
		0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
		0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
		0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
		0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
		0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
		0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
		0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
		0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
		0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
		0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
		0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
		0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
		0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
		0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
		0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
	};
	long dlen;
	char *ok;

	printf("Testing %s...\n", testfn);

	/* NIST: "Keyed-Hash Message Authentication Code (HMAC) using SHA3-256" (HMAC_SHA3-256.pdf) */

	printf("Sample #1\n");
	data = (unsigned char*)"Sample message for keylen<blocklen";
	dlen = (long)strlen(data);
	ok = "4fe8e202c4f058e8dddc23d8c34e467343e23555e24fc2f025d598f558f67205";
	printf("DATA='%s'\n", data);

	lRet = HMAC_HexFromBytes(szDigest, sizeof(szDigest) - 1, data, dlen, key1, sizeof(key1), PKI_HMAC_SHA3_256);
	assert(lRet > 0);
	printf("HMAC-SHA-3-256()=%s\n", szDigest);
	assert(strcmp(szDigest, ok) == 0);

	printf("Sample #3\n");
	data = (unsigned char*)"Sample message for keylen>blocklen";
	dlen = (long)strlen(data);
	ok = "9bcf2c238e235c3ce88404e813bd2f3a97185ac6f238c63d6229a00b07974258";
	printf("DATA='%s'\n", data);

	lRet = HMAC_HexFromBytes(szDigest, sizeof(szDigest) - 1, data, dlen, key3, sizeof(key3), PKI_HMAC_SHA3_256);
	assert(lRet > 0);
	printf("HMAC-SHA-3-256()=%s\n", szDigest);
	assert(strcmp(szDigest, ok) == 0);

	/* NIST: "Keyed-Hash Message Authentication Code (HMAC) using SHA3-512" (HMAC_SHA3-512.pdf) */
	printf("SHA3-512/Sample #3\n");
	data = (unsigned char*)"Sample message for keylen>blocklen";
	dlen = (long)strlen(data);
	ok = "5f464f5e5b7848e3885e49b2c385f0694985d0e38966242dc4a5fe3fea4b37d46b65ceced5dcf59438dd840bab22269f0ba7febdb9fcf74602a35666b2a32915";
	printf("DATA='%s'\n", data);

	lRet = HMAC_HexFromBytes(szDigest, sizeof(szDigest) - 1, data, dlen, key512_3, sizeof(key512_3), PKI_HMAC_SHA3_512);
	assert(lRet > 0);
	printf("HMAC-SHA-3-512()=%s\n", szDigest);
	assert(strcmp(szDigest, ok) == 0);

	printf("...%s tested OK\n", testfn);
}

void test_XOF(void)
{
	char *testfn = "test_XOF()";

	long r;
	unsigned char msg1[] = {
		0x59, 0xFA, 0x3D, 0x70, 0x91, 0xED, 0xD1, 0xAE,
		0x28, 0x74, 0x94, 0x9A, 0x0B, 0x18, 0xFF, 0x95,
	};
	unsigned char msg2[] = {
		0xC6, 0x1A, 0x91, 0x88, 0x81, 0x2A, 0xE7, 0x39,
		0x94, 0xBC, 0x0D, 0x6D, 0x40, 0x21, 0xE3, 0x1B,
		0xF1, 0x24, 0xDC, 0x72, 0x66, 0x97, 0x49, 0x11,
		0x12, 0x32, 0xDA, 0x7A, 0xC2, 0x9E, 0x61, 0xC4,
	};
	unsigned char msg3[] = {
		0x6A, 0xE2, 0x3F, 0x05, 0x8F, 0x0F, 0x22, 0x64,
		0xA1, 0x8C, 0xD6, 0x09, 0xAC, 0xC2, 0x6D, 0xD4,
		0xDB, 0xC0, 0x0F, 0x5C, 0x3E, 0xE9, 0xE1, 0x3E,
		0xCA, 0xEA, 0x2B, 0xB5, 0xA2, 0xF0, 0xBB, 0x6B,
	};
	unsigned char output[1024];
	unsigned char *pmsg;
	long mlen, olen;
	char *okhex;
	long flag;

	printf("\nTesting %s...\n", testfn);

	flag = PKI_XOF_SHAKE128;
	pmsg = msg1;
	mlen = sizeof(msg1);
	olen = 464 / 8;
	okhex = "7b27fd12ce28aadd5fe136cd26fadb9e26b0ec0858c5599bd599a17ba36f032d5fb55b50effa08fd423b9e28e780c16066bc9b806a7f646db20e";
	printf("SHAKE%d\n", (PKI_XOF_SHAKE256 == flag ? 256 : 128));
	pr_byteshexwrap(32, "Msg =\n", pmsg, mlen, "\n");
	printf("Outputlen = %d\n", olen * 8);

	r = XOF_Bytes(output, olen, pmsg, mlen, flag);
	assert(r > 0);
	pr_byteshexwrap(32, "Output =\n", output, olen, "\n");
	pr_wrapstr("OK:\n", okhex, 64);
	assert(0 == cmp_bytes_with_hex(output, olen, okhex));

	flag = PKI_XOF_SHAKE256;
	pmsg = msg2;
	mlen = sizeof(msg2);
	olen = 16 / 8;
	okhex = "23ce";
	printf("SHAKE%d\n", (PKI_XOF_SHAKE256 == flag ? 256 : 128));
	pr_byteshexwrap(32, "Msg =\n", pmsg, mlen, "\n");
	printf("Outputlen = %d\n", olen * 8);

	r = XOF_Bytes(output, olen, pmsg, mlen, flag);
	assert(r > 0);
	pr_byteshexwrap(32, "Output =\n", output, olen, "\n");
	pr_wrapstr("OK:\n", okhex, 64);
	assert(0 == cmp_bytes_with_hex(output, olen, okhex));

	flag = PKI_XOF_SHAKE256;
	pmsg = msg3;
	mlen = sizeof(msg3);
	olen = 2000 / 8;
	okhex = "b9b92544fb25cfe4ec6fe437d8da2bbe00f7bdaface3de97b8775a44d753c3adca3f7c6f183cc8647e229070439aa9539ae1f8f13470c9d3527fffdeef6c94f9f0520ff0c1ba8b16e16014e1af43ac6d94cb7929188cce9d7b02f81a2746f52ba16988e5f6d93298d778dfe05ea0ef256ae3728643ce3e29c794a0370e9ca6a8bf3e7a41e86770676ac106f7ae79e67027ce7b7b38efe27d253a52b5cb54d6eb4367a87736ed48cb45ef27f42683da140ed3295dfc575d3ea38cfc2a3697cc92864305407369b4abac054e497378dd9fd0c4b352ea3185ce1178b3dc1599df69db29259d4735320c8e7d33e8226620c9a1d22761f1d35bdff79a";
	printf("SHAKE%d\n", (PKI_XOF_SHAKE256 == flag ? 256 : 128));
	pr_byteshexwrap(32, "Msg =\n", pmsg, mlen, "\n");
	printf("Outputlen = %d\n", olen * 8);

	r = XOF_Bytes(output, olen, pmsg, mlen, flag);
	assert(r > 0);
	pr_byteshexwrap(32, "Output =\n", output, olen, "\n");
	pr_wrapstr("OK:\n", okhex, 64);
	assert(0 == cmp_bytes_with_hex(output, olen, okhex));

}

void test_XOF_MGF1(void)
{
	char *testfn = "test_XOF_MGF1()";

	long r;
	unsigned char input1[] = {
		0x3B, 0x5C, 0x05, 0x6A, 0xF3, 0xEB, 0xBA, 0x70,
		0xD4, 0xC8, 0x05, 0x38, 0x04, 0x20, 0x58, 0x55,
		0x62, 0xB3, 0x24, 0x10, 0xA7, 0x78, 0xF5, 0x58,
		0xFF, 0x95, 0x12, 0x52, 0x40, 0x76, 0x47, 0xE3,
	};
	unsigned char input2[] = {
		0x01, 0x23, 0x45, 0xFF,
	};
	unsigned char output[1024];
	unsigned char *pmsg;
	long mlen, olen;
	char *okhex;
	long flag;

	printf("Testing %s...\n", testfn);

	printf("MGF1 from PKCS#5 is an eXtendable-Output Function (XOF)\n");

	flag = PKI_XOF_MGF1_SHA1;
	printf("HashAlg=SHA-1\n");
	pmsg = input2;
	mlen = sizeof(input2);
	olen = 24;
	okhex = "242fb2e7a338ae07e580047f82b7acff83a41ec5d8ff9bab";
	pr_byteshexwrap(32, "Msg:\n", pmsg, mlen, "\n");
	printf("Outputlen = %d bytes\n", olen);
	r = XOF_Bytes(output, olen, pmsg, mlen, flag);
	assert(r > 0);
	pr_byteshexwrap(32, "Output:\n", output, olen, "\n");
	pr_wrapstr("OK:\n", okhex, 64);
	assert(0 == cmp_bytes_with_hex(output, olen, okhex));

	flag = PKI_XOF_MGF1_SHA256;
	printf("HashAlg=SHA-256\n");
	pmsg = input1;
	mlen = sizeof(input1);
	olen = 34;
	okhex = "5b7eb772aecf04c74af07d9d9c1c1f8d3a90dcda00d5bab1dc28daecdc86eb87611e";
	pr_byteshexwrap(32, "Msg:\n", pmsg, mlen, "\n");
	printf("Outputlen = %d bytes\n", olen);
	r = XOF_Bytes(output, olen, pmsg, mlen, flag);
	assert(r > 0);
	pr_byteshexwrap(36, "Output:\n", output, olen, "\n");
	pr_wrapstr("OK:\n", okhex, 72);
	assert(0 == cmp_bytes_with_hex(output, olen, okhex));

	printf("...%s tested OK\n", testfn);
}


void test_PRF(void)
{
	char *testfn = "test_PRF()";

	long r;
	unsigned char key[] = {
		0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
		0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
		0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
		0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
	};
	unsigned char datashort[] = { 0x00, 0x01, 0x02, 0x03, };
	unsigned char datalong[] = {
		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
		0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
		0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
		0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
		0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
		0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
		0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
		0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
		0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
		0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
		0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
		0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
		0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
		0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
		0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
		0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
		0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
		0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
		0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
		0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
		0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
		0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7,
		0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
		0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
	};
	unsigned char output[1024];
	unsigned char *pdata;
	long dlen, klen, olen;
	char *okhex;
	long flag;
	char *customstring;

	printf("\nTesting %s...\n", testfn);


	// Ref: `KMAC_samples.pdf` "Secure Hashing - KMAC-Samples" 2017-02-27
	// <https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/KMAC_samples.pdf>

	printf("Sample #2\n");
	flag = PKI_KMAC_128;
	pdata = datashort;
	dlen = sizeof(datashort);
	klen = sizeof(key);
	olen = 256 / 8;
	customstring = "My Tagged Application";
	okhex = "3B1FBA963CD8B0B59E8C1A6D71888B7143651AF8BA0A7070C0979E2811324AA5";

	printf("Security Strength: %d-bits\n", (PKI_KMAC_256 == flag ? 256 : 128));
	printf("Length of Key is %d bits\n", klen * 8);
	pr_byteshex16("KEY:\n", key, klen, "\n");
	printf("Length of data is %d bits\n", dlen * 8);
	pr_byteshex16("DATA:\n", pdata, dlen, "\n");
	printf("Requested output length is %d bits\n", olen * 8);
	printf("S (as a character string) is\n\"%s\"\n", customstring);

	/* To use a customization string with KMAC, use PRF_Bytes*/
	r = PRF_Bytes(output, olen, pdata, dlen, key, klen, customstring, flag);
	assert(r > 0);
	pr_byteshex16("OUTPUT:\n", output, olen, "\n");
	pr_wrapstr("OK:\n", okhex, 32);
	assert(0 == cmp_bytes_with_hex(output, olen, okhex));

	// Ask for longer output
	olen = 1600 / 8;
	customstring = "";
	okhex = "38158A1CAE4E1A25D85F2031246ADE697B3292FEF88B0923A59A02D1D53B704653EE7242662A10796BA20779D300D52D7432018741233D587252D31DC48BDB8233285D4A4ACD65848509B051A448D873649228B6626E5EF817C7AF2DEDC91F120F8CA535A1EE301FAE8186FDEDE5A76181A472A32CFAD1DDD1391E162F124D4A7572AD8A20076601BCF81E4B0391F3E95AEFFA708C33C1217C96BE6A4F02FBBC2D3B3B6FFAEB5BFD3BE4A2E02B75993FCC04DA6FAC4BFCB2A9F05792A1A5CC80CA34186243EFDB31";
	printf("Requested output length is %d bits\n", olen * 8);
	printf("S (as a character string) is\n\"%s\"\n", customstring);
	r = PRF_Bytes(output, olen, pdata, dlen, key, klen, customstring, flag);
	assert(r > 0);
	pr_byteshex16("OUTPUT:\n", output, olen, "\n");
	//pr_wrapstr("OK:\n", okhex, 32);
	assert(0 == cmp_bytes_with_hex(output, olen, okhex));

}


int main(int argc, char *argv[])
{
	int c;

/* 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_STDOUT );
	_CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE );
	_CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDOUT );
	_CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE );
	_CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDOUT );
#endif

	/* Setup... */
	check_user_options(argc, argv);
	// 
	make_new_test_dir();
	create_test_files();

	/* Do the tests...*/
	show_version();
	init_rng();
	carol_creates_keys();
	carol_creates_ca_cert();
	ann_and_ben_create_keys();
	carol_makes_certs_for_ann_and_ben();
	ben_tests_his_keys_in_the_raw();
	ann_sends_enc_msg_to_ben();
	ben_decrypts_msg_from_ann();
	ann_sends_encmsg_ben_reads();
	ann_sends_encr_base64_ben_reads();
	ann_signs_msg();
	ben_verifies_msg_from_ann();
	ben_reads_signed_data();
	ben_signs_det_msg();
	ann_verifies_det_msg_from_ben();
	ann_signs_base64_ben_verifies();
	ben_makes_crs();
	ann_verifies_her_cert();
	ben_resaves_his_public_key();
	if (!no_prompt) ben_examines_his_private_key();
	carol_reads_public_key_from_cert();
	ann_makes_cert_chain();
	ben_makes_pfx();
	ben_encrypts_raw_key();
	ann_decrypts_raw_key();
	ann_signs_raw_msg();
	ben_verifies_raw_msg();
	ben_creates_cjk_ca_cert();
	/* Added in [v3.5] */
	damien_creates_csr();
	carol_makes_cert_from_damiens_csr();
	damien_reads_new_p7_chain_file();
	carol_revokes_damiens_cert();
	we_check_damiens_cert_in_crl();
	/* Added in [v3.6] */
	// Windows only
#if defined(_WIN32) || defined(WIN32) 
	test_byte_encoding();
	test_utf8bytes_from_latin1();
#endif
	/* Added in [v10.0] */
	check_asn1();
	/* Added in [v11.0]*/
	ecc_makekeys_and_sign();
	ecc_bitcoin_raw_verify();
	byte_utilities();
	/* Added in [v12.0] */
	compress_data();
	/* Added in [v12.1] */
	read_cert_from_pfx_and_p7c();
	/* Added in [v12.2] */
	read_rsa_from_jws();
	/* Added in [v20.0] */
	cipher_encrypt_hex();
	sign_data_Ed25519();
	x509_cert_25519();
	ecdh_p256();
	ecdh_X25519();
	cms_makesig_pseudo();
	/* Added in [v20.4] */
	ecc_brainpool();
	/* Added in [v20.5] */
	test_KDF_Hkdf();
	test_KDF_X963();
	test_CMS_MakeEnvData_ECDH();
	test_X509_MakeCert_InternalKeys_25519_encrypt();
	/* Added in [v20.6] */
	test_CMS_MakeEnvData_adv();
	/* Added in [v21.0] */
	test_CNV_ShortPathName();
	test_CIPHER_Encrypt_GCM();
	test_RSA_ReadPublicKey_CSR();
	test_X509_MakeCert_Ex();
	test_FormatErrorMessage();
	test_HASH_HexFromBytes_SHA3();
	test_SHA3_HMAC();
	test_PRF();
	test_XOF();
	test_XOF_MGF1();

	printf("\nCore PKI DLL version = %ld\n", PKI_Version(0,0));
	
	printf("\nALL DONE.\n");

	if (!no_prompt) {
		/* Option to clean up - default is Yes */
		printf("The directory '%s' has been created by this test program.\n"
			"Do you want to remove this directory? [Y]/N: ", testdir);
		c = getchar();
		if (c != 'N' && c != 'n')
			remove_test_dir(testdir, old_cwd);
	}
	else {
		remove_test_dir(testdir, old_cwd);
	}

#if _MSC_VER >= 1100
	_CrtDumpMemoryLeaks();
#endif

	return 0;
}