/*  $Id: TestPKI.cpp $
*   Last updated:
*   $Date: 2023-01-01 03:23:00 $
*   $Version: 21.0.0 $
*/

// Some tests using the CryptoSys PKI C++ (STL) interface.
// Requires certain files to exist in the current working directory 

/******************************* LICENSE ***********************************
* Copyright (C) 2022-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.
* For a copy, see <http://opensource.org/licenses/MIT>
****************************************************************************
*/
#include <string>
#include <cwchar>
#include <iostream>
#include <clocale>
#include <assert.h>
#include <fstream>
#include <sstream>
#include <sys/stat.h>	// For the stat() function used in file_length()
#include "dipki.hpp"

#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>	// For ``SetConsoleOutputCP(CP_UTF8);``
#endif

using std::cout;
using std::endl;

// Global vars set by command-line args
static bool doPrompt = false;

// UTILITY FUNCTIONS

/** Read in a file to a string in one go. */
static std::string read_text_file(const std::string& path) {
	std::ifstream in(path);
	auto ss = std::ostringstream{};
	ss << in.rdbuf();
	return ss.str();
}

/** Read in a binary file. */
static dipki::bvec_t read_binary_file(const std::string& path) {
	std::ifstream stream(path, std::ios::in | std::ios::binary);
	dipki::bvec_t bv((std::istreambuf_iterator<char>(stream)), std::istreambuf_iterator<char>());
	return bv;
}

/** Write string to a text file */
static void write_text_file(const std::string& path, const std::string& s) {
	std::ofstream f(path);
	f << s;
	f.close();
}

/** Write to a binary file file */
static void write_binary_file(const std::string& path, const dipki::bvec_t& bv) {
	std::ofstream f(path, std::ios::binary);
	f.write(reinterpret_cast<const char*>(bv.data()), bv.size());
	f.close();
}

/** Read just the first line from a text file */
static std::string read_first_line(const std::string& path) {
	std::ifstream in(path);
	std::string line;
	std::getline(in, line);
	return line;
}

/** Get size of file */
static long file_length(const std::string& path) {
	struct stat stat_buf;
	int rc = stat(path.c_str(), &stat_buf);
	return rc == 0 ? stat_buf.st_size : -1;
}

/** Format a boolean value */
inline const std::string bool2str(bool b)
{
	return b ? "true" : "false";
}

// THE TESTS...

static void test_Cnv() {
	dipki::bvec_t bv;
	cout << "\nTESTING Cnv..." << endl;
	cout << dipki::Cnv::ToHex("abc") << endl;
	cout << dipki::Cnv::ToHex(dipki::Cnv::FromHex("616263")) << endl;
	cout << dipki::Cnv::ToHex(dipki::Cnv::FromBase64("/ty6mHZUMhA=")) << endl;
	cout << dipki::Cnv::ToBase64(dipki::Cnv::FromBase64("/ty6mHZUMhA=")) << endl;
	cout << dipki::Cnv::ToBase64("abc") << endl;

	// Punct and spaces are ignored
	bv = dipki::Cnv::FromHex(" 61 : 62 :63 ");
	cout << "bv.size=" << bv.size() << endl;

	cout << "BEGIN EXPECTING ERRORS..." << endl;
	try {
		bv = dipki::Cnv::FromHex("BADHEXXX");
		cout << "bv.size=" << bv.size() << endl;
	}
	catch (std::exception& e) {
		cout << e.what() << endl;
	}
	try {
		bv = dipki::Cnv::FromBase64("BADBASE64%#@");
		cout << "bv.size=" << bv.size() << endl;
	}
	catch (std::exception& e) {
		cout << e.what() << endl;
	}
	cout << "...END ERRORS" << endl;


}

void test_Cnv_byteutils()
{
	dipki::bvec_t bv;
	cout << "\nTESTING CNV BYTE UTILS..." << endl;

	cout << "Testing Cnv::ReverseBytes()..." << endl;
	dipki::bvec_t b{ 0x01, 0x02, 0x03, 0x04, 0x05 };
	cout << "(before)=" << dipki::Cnv::ToHex(b) << endl;
	// (before)=0102030405
	b = dipki::Cnv::ReverseBytes(b);
	cout << "(after) =" << dipki::Cnv::ToHex(b) << endl;
	// (after) =0504030201

	assert(b[0] == 0x05 && b[4] == 0x01);

	cout << "Testing Cnv::NumToBytes()..." << endl;
	bv = dipki::Cnv::NumToBytes(0xdeadbeef);
	cout << "Cnv::NumToBytes(0xdeadbeef,BE)->" + dipki::Cnv::ToHex(bv) << endl;
	// Cnv::NumToBytes(0xdeadbeef,BE)->DEADBEEF

	bv = dipki::Cnv::NumToBytes(0xdeadbeef, dipki::Cnv::EndianNess::LittleEndian);
	cout << "Cnv::NumToBytes(0xdeadbeef,LE)->" + dipki::Cnv::ToHex(bv) << endl;
	// Cnv::NumToBytes(0xdeadbeef,LE)->EFBEADDE

	cout << "Testing Cnv::NumFromBytes()..." << endl;
	bv = dipki::Cnv::FromHex("DEADBEEF");
	uint32_t ui = dipki::Cnv::NumFromBytes(bv);
	cout << "Cnv::NumFromBytes(" << dipki::Cnv::ToHex(bv) << ")->" << ui << "=0x" << std::hex << ui << std::dec << endl;
	// Cnv::NumFromBytes(DEADBEEF)->3735928559=0xdeadbeef

	bv = dipki::Cnv::FromHex("DEAD");	// Short array
	ui = dipki::Cnv::NumFromBytes(bv);
	cout << "Cnv::NumFromBytes(" << dipki::Cnv::ToHex(bv) << ")->" << ui << "=0x" << std::hex << ui << std::dec << endl;
	// Cnv::NumFromBytes(DEAD)->3735879680=0xdead0000

}

void test_Cnv_base58()
{
	std::string s, b58str;
	dipki::bvec_t b;
	cout << "\nBASE58 CONVERSION TESTS:" << endl;
	b58str = "16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM";
	b = dipki::Cnv::FromBase58(b58str);
	cout << "'" + b58str + "'->\n0x" + dipki::Cnv::ToHex(b) << endl;
	// Back to base58
	s = dipki::Cnv::ToBase58(b);
	cout << "In base58='" + s + "'" << endl;
	assert(s == b58str);
	// Convert directly from base58 to hex
	s = dipki::Cnv::ToHex(dipki::Cnv::FromBase58(b58str));
	cout << "In hex='" + s + "'" << endl;
	assert(s == "00010966776006953D5567439E5E39F86A0D273BEED61967F6");
	// One-liner
	cout << "Cnv::FromBase58(\"16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM\")=0x" << dipki::Cnv::ToHex(dipki::Cnv::FromBase58("16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM")) << endl;
	// Cnv::FromBase58("16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM")=0x00010966776006953D5567439E5E39F86A0D273BEED61967F6
}

void test_Cnv_utf8()
{
	dipki::bvec_t bv;
	int n;
	std::string s;
	std::wstring wstr;
	std::string fname;
	cout << "\nUTF-8 CONVERSION TESTS:" << endl;
	// Create a string that includes some Latin-1 accented characters:
	s = "abcóéíáñ";
	// This should contain the following 8 characters:
	//  U+0061 LATIN SMALL LETTER A
	//  U+0062 LATIN SMALL LETTER B
	//  U+0063 LATIN SMALL LETTER C
	//  U+00F3 LATIN SMALL LETTER O WITH ACUTE
	//  U+00E9 LATIN SMALL LETTER E WITH ACUTE
	//  U+00ED LATIN SMALL LETTER I WITH ACUTE
	//  U+00E1 LATIN SMALL LETTER A WITH ACUTE
	//  U+00F1 LATIN SMALL LETTER N WITH TILDE
	cout << "[" << s << "]" << "(missing!)" << endl;	// Aha! Does not show all the characters because they are not UTF-8, which we set the console for in main().
	cout << dipki::Cnv::ToHex(dipki::str2bvec(s)) << endl;	// Shows all 8 characters are there, but in Latin-1 encoding.
	n = dipki::Cnv::CheckUTF8(dipki::str2bvec(s));
	cout << "Cnv::CheckUTF8=" << n << " (expecting 0)==> " << dipki::Cnv::CheckUTF8CodeAsString(n) << endl;
	bv = dipki::Cnv::UTF8BytesFromLatin1(s);
	cout << dipki::Cnv::ToHex(bv) << endl;
	cout << dipki::bvec2str(bv) << endl;
	n = dipki::Cnv::CheckUTF8(bv);
	cout << "Cnv::CheckUTF8=" << n << " (expecting 2)==> " << dipki::Cnv::CheckUTF8CodeAsString(n) << endl;
	// Convert back to Latin-1
	std::string latin1str = dipki::Cnv::Latin1FromUTF8Bytes(bv);
	cout << dipki::Cnv::ToHex(dipki::str2bvec(latin1str)) << endl;
	// Plain ASCII
	n = dipki::Cnv::CheckUTF8(dipki::str2bvec("abc"));
	cout << "Cnv::CheckUTF8=" << n << " (expecting 1)==> " << dipki::Cnv::CheckUTF8CodeAsString(n) << endl;

	// Chinese - hardcoded UTF-8 bytes
	// U+4E2D U+56FD => E4 B8 AD E5 9B BD
	dipki::bvec_t b{ 0xe4, 0xb8, 0xad, 0xE5, 0x9B, 0xBD };
	n = dipki::Cnv::CheckUTF8(b);
	cout << "Cnv::CheckUTF8=" << n << " (expecting 3)==> " << dipki::Cnv::CheckUTF8CodeAsString(n) << endl;
	// Try (wrongly) to convert bytes to Latin-1
	s = dipki::Cnv::Latin1FromUTF8Bytes(b);
	cout << s << endl;

	// Save to file then check the file
	fname = "zhongguo.txt";
	write_binary_file(fname, b);
	cout << "FILE: " << fname << endl;
	n = dipki::Cnv::CheckUTF8File(fname);
	cout << "Cnv::CheckUTF8File=" << n << " (expecting 3)==> " << dipki::Cnv::CheckUTF8CodeAsString(n) << endl;

	// Mapping wide chars to a UTF-8-encoded string
	cout << "Map UTF-16 (wide) characters to UTF-8..." << endl;
	// EXAMPLE using Polish characters
	// ę 'LATIN SMALL LETTER E WITH OGONEK' (U+0119) => 0xC4 0x99
	// ł 'LATIN SMALL LETTER L WITH STROKE' (U+0142) => 0xC5 0x82
	cout << "Polish:" << endl;
	wstr = L"węzeł";
	s = dipki::Cnv::Utf8FromWide(wstr);
	cout << "Cnv::Utf8FromWide=[" << s << "]" << endl;
	cout << "s-->utf-8 bytes: " << dipki::Cnv::ToHex(dipki::str2bvec(s)) << endl;
	// Spanish
	cout << "Spanish:" << endl;
	wstr = L"áéíñóü";
	s = dipki::Cnv::Utf8FromWide(wstr);
	cout << "Cnv::Utf8FromWide=[" << s << "]" << endl;
	cout << "s-->utf-8 bytes: " << dipki::Cnv::ToHex(dipki::str2bvec(s)) << endl;
	// This won't display properly on a Western Windows platform, but the UTF-8 bytes are correct
	cout << "Chinese:" << endl;
	wstr = L"中国";    // U+4E2D U+56FD => E4 B8 AD E5 9B BD 
	s = dipki::Cnv::Utf8FromWide(wstr);
	cout << "Cnv::Utf8FromWide=[" << s << "] <= NOTE Chinese chars may not display properly" << endl;
	cout << "but the UTF-8 bytes are correct..." << endl;
	cout << "s-->utf-8 bytes: " << dipki::Cnv::ToHex(dipki::str2bvec(s)) << endl;
	cout << "U+4E2D U+56FD => E4 B8 AD E5 9B BD" << endl;

	// One-liner
	cout << dipki::Cnv::ToHex(dipki::Cnv::Utf8FromWide(L"中国")) << endl;
	// E4B8ADE59BBD U+4E2D U+56FD => E4 B8 AD E5 9B BD
	cout << dipki::Cnv::ToHex(dipki::Cnv::Utf8FromWide(L"áéíñóü")) << endl;
	// C3A1C3A9C3ADC3B1C3B3C3BC
}

void test_Cipher() {
	//std::string s;
	dipki::bvec_t bv;
	dipki::bvec_t pt, ct, dt, key, iv, data;
	std::string algstr;

	cout << "\nTESTING Cipher..." << endl;
	algstr = "Aes128/CBC/pkcs5";
	cout << algstr << endl;
	key = dipki::Cnv::FromHex("0123456789ABCDEFF0E1D2C3B4A59687");
	iv = dipki::Cnv::FromHex("FEDCBA9876543210FEDCBA9876543210");
	pt = dipki::str2bvec("Now is the time for all good men to");
	ct = dipki::Cipher::Encrypt(pt, key, iv, algstr);
	cout << "ct=" << dipki::Cnv::ToHex(ct) << endl;
	assert(0 == dipki::Cnv::ToHex(ct).compare("C3153108A8DD340C0BCB1DFE8D25D2320EE0E66BD2BB4A313FB75C5638E9E17753C7E8DF5975A36677355F5C6584228B"));
	dt = dipki::Cipher::Decrypt(ct, key, iv, algstr);
	cout << "dt='" << dipki::bvec2str(dt) << "'" << endl;
	assert(dt.size() > 0);

	ct = dipki::Cipher::Encrypt(pt, key, iv, algstr, dipki::Cipher::Opts::PrefixIV);
	cout << "ct(PrefixIV)=" << dipki::Cnv::ToHex(ct) << endl;
	//assert(0 == dipki::Cnv::ToHex(ct).compare("C3153108A8DD340C0BCB1DFE8D25D2320EE0E66BD2BB4A313FB75C5638E9E17753C7E8DF5975A36677355F5C6584228B"));
	dt = dipki::Cipher::Decrypt(ct, key, iv, algstr, dipki::Cipher::Opts::PrefixIV);
	cout << "dt='" << dipki::bvec2str(dt) << "'" << endl;
	assert(dt.size() > 0);

	cout << "bvec2str(empty)='" << dipki::bvec2str(dipki::bvec_t()) << "'" << endl;

	// Same input, different mode and padding, ECB mode so pass an empty IV
	algstr = "Aes128/ECB/OneAndZeroes";
	cout << algstr << endl;
	ct = dipki::Cipher::Encrypt(pt, key, dipki::bvec_t(), algstr);
	cout << "ct=" << dipki::Cnv::ToHex(ct) << endl;
	assert(0 == dipki::Cnv::ToHex(ct).compare("F0D1AD6F901FFFAE5572A6928DAB52B064B25C79F876730321E36DC01011ACCED7E53F5E5CB18233FE486CD4FFA79FE9"));
	dt = dipki::Cipher::Decrypt(ct, key, iv, algstr);
	cout << "dt='" << dipki::bvec2str(dt) << "'" << endl;
	assert(dt.size() > 0);

	// Again, using Alg/Mode/Pad params instead of alg string
	ct = dipki::Cipher::Encrypt(pt, key, iv, dipki::Cipher::Alg::Aes128, dipki::Cipher::Mode::CBC, dipki::Cipher::Padding::Default);
	cout << "ct=" << dipki::Cnv::ToHex(ct) << endl;
	assert(0 == dipki::Cnv::ToHex(ct).compare("C3153108A8DD340C0BCB1DFE8D25D2320EE0E66BD2BB4A313FB75C5638E9E17753C7E8DF5975A36677355F5C6584228B"));
	dt = dipki::Cipher::Decrypt(ct, key, iv, dipki::Cipher::Alg::Aes128, dipki::Cipher::Mode::CBC, dipki::Cipher::Padding::Default);
	cout << "dt='" << dipki::bvec2str(dt) << "'" << endl;
	assert(dt.size() > 0);

	ct = dipki::Cipher::Encrypt(pt, key, dipki::bvec_t(), dipki::Cipher::Alg::Aes128, dipki::Cipher::Mode::ECB, dipki::Cipher::Padding::OneAndZeroes);
	cout << "ct=" << dipki::Cnv::ToHex(ct) << endl;
	assert(0 == dipki::Cnv::ToHex(ct).compare("F0D1AD6F901FFFAE5572A6928DAB52B064B25C79F876730321E36DC01011ACCED7E53F5E5CB18233FE486CD4FFA79FE9"));
	dt = dipki::Cipher::Decrypt(ct, key, dipki::bvec_t(), dipki::Cipher::Alg::Aes128, dipki::Cipher::Mode::ECB, dipki::Cipher::Padding::OneAndZeroes);
	cout << "dt='" << dipki::bvec2str(dt) << "'" << endl;
	assert(dt.size() > 0);

	cout << "TEST CIPHER FUNCTIONS WITH EXACT BLOCK LENGTHS..." << endl;
	key = dipki::Cnv::FromHex("0123456789ABCDEFF0E1D2C3B4A59687");
	iv = dipki::Cnv::FromHex("FEDCBA9876543210FEDCBA9876543210");
	pt = dipki::str2bvec("Now is the time for all good men");	// Exactly 32 bytes, a multiple of 16
	ct = dipki::Cipher::EncryptBlock(pt, key, iv, dipki::Cipher::Alg::Aes128, dipki::Cipher::Mode::CBC);
	cout << "ct=" << dipki::Cnv::ToHex(ct) << endl;
	assert(0 == dipki::Cnv::ToHex(ct).compare("C3153108A8DD340C0BCB1DFE8D25D2320EE0E66BD2BB4A313FB75C5638E9E177"));
	dt = dipki::Cipher::DecryptBlock(ct, key, iv, dipki::Cipher::Alg::Aes128, dipki::Cipher::Mode::CBC);
	cout << "dt=" << dipki::Cnv::ToHex(dt) << endl;
	cout << "dt='" << dipki::bvec2str(dt) << "'" << endl;
	assert(dt.size() > 0);

	// Use defaults and a random key
	dipki::Cipher::Alg myalg = dipki::Cipher::Alg::Aes256;	// Cipher::Alg _must_ be specified
	cout << "Cipher::Alg = " << dipki::Cipher::AlgName(myalg) << endl;
	key = dipki::Rng::Bytes(dipki::Cipher::KeyBytes(myalg));
	cout << "Random key: " << dipki::Cnv::ToHex(key) << endl;
	ct = dipki::Cipher::EncryptBlock(pt, key, iv, myalg);
	cout << "ct=" << dipki::Cnv::ToHex(ct) << endl;
	dt = dipki::Cipher::DecryptBlock(ct, key, iv, myalg);
	cout << "dt=" << dipki::Cnv::ToHex(dt) << endl;
	cout << "dt='" << dipki::bvec2str(dt) << "'" << endl;
	assert(dt.size() > 0);

	cout << "BEGIN EXPECTING ERRORS..." << endl;
	// Invalid decryption
	cout << "Expecting an error (passing the wrong key to decrypt)..." << endl;
	key = dipki::Cnv::FromHex("FF0123456789ABCDEFF0E1D2C3B4A596");	// Wrong key
	iv = dipki::Cnv::FromHex("FEDCBA9876543210FEDCBA9876543210");
	ct = dipki::Cnv::FromHex("C3153108A8DD340C0BCB1DFE8D25D2320EE0E66BD2BB4A313FB75C5638E9E17753C7E8DF5975A36677355F5C6584228B");

	try {
		dt = dipki::Cipher::Decrypt(ct, key, iv, dipki::Cipher::Alg::Aes128, dipki::Cipher::Mode::CBC);
		cout << "dt=" << dipki::Cnv::ToHex(dt) << endl;
	}
	catch (std::exception& e) {
		cout << e.what() << endl;
	}
	cout << "Passing an invalid IV..." << endl;
	try {
		dt = dipki::Cipher::EncryptBlock(ct, key, dipki::bvec_t(), dipki::Cipher::Alg::Aes128, dipki::Cipher::Mode::CBC);
		cout << "dt=" << dipki::Cnv::ToHex(dt) << endl;
	}
	catch (std::exception& e) {
		cout << e.what() << endl;
	}
	cout << "... END ERRORS" << endl;

	// One-liners
	cout << "Cipher::BlockBytes(dipki::Cipher::Alg::Aes256)=" << dipki::Cipher::BlockBytes(dipki::Cipher::Alg::Aes256) << endl;
	// Cipher::BlockBytes(dipki::Cipher::Alg::Aes256)=16
	cout << "Cipher::KeyBytes(dipki::Cipher::Alg::Aes256)=" << dipki::Cipher::KeyBytes(dipki::Cipher::Alg::Aes256) << endl;
	// Cipher::KeyBytes(dipki::Cipher::Alg::Aes256)=32
	cout << "Cipher::AlgName(dipki::Cipher::Alg::Aes256)=" << dipki::Cipher::AlgName(dipki::Cipher::Alg::Aes256) << endl;
	// Cipher::AlgName(dipki::Cipher::Alg::Aes256)=AES-256
}

void test_Cipher_File() {
	dipki::bvec_t key, iv;
	std::string fnameData, fnameEnc, fnameCheck;
	dipki::bvec_t bv;
	int r;
	cout << "\nTESTING Cipher_File..." << endl;
	key = dipki::Cnv::FromHex("fedcba98765432100123456789abcdeffedcba9876543210");
	fnameData = "excontent.txt";
	fnameEnc = "excontent.tdea.enc.dat";
	r = dipki::Cipher::FileEncrypt(fnameEnc, fnameData, key, dipki::bvec_t(), dipki::Cipher::Alg::Tdea);
	cout << "Created file " << fnameEnc << endl;
	bv = read_binary_file(fnameEnc);
	cout << "CT=" << dipki::Cnv::ToHex(bv) << endl;
	assert(dipki::Cnv::ToHex(bv) == "DD1E1FA430AE6BE1D3B83245F7A5B17C4BF03688238778E95F2CCD05AF1A8F44");
	// Decrypt the file
	fnameCheck = "excontent.tdea.chk.txt";
	r = dipki::Cipher::FileDecrypt(fnameCheck, fnameEnc, key, dipki::bvec_t(), dipki::Cipher::Alg::Tdea);
	cout << "Created file " << fnameCheck << endl;
	bv = read_binary_file(fnameCheck);
	cout << "DT=" << dipki::Cnv::ToHex(bv) << endl;
	cout << "DT='" << dipki::bvec2str(bv) << "'" << endl;

	key = dipki::Cnv::FromHex("fedcba98765432100123456789abcdef");
	iv = dipki::Cnv::FromHex ("100000000000000000000000000000FF");
	fnameData = "excontent.txt";
	fnameEnc = "excontent.aes128.enc.dat";
	r = dipki::Cipher::FileEncrypt(fnameEnc, fnameData, key, iv, dipki::Cipher::Alg::Aes128, dipki::Cipher::Mode::CTR, dipki::Cipher::Padding::Default, dipki::Cipher::Opts::PrefixIV);
	cout << "Created file " << fnameEnc << endl;
	bv = read_binary_file(fnameEnc);
	cout << "CT=" << dipki::Cnv::ToHex(bv) << endl;
	assert(dipki::Cnv::ToHex(bv) == "100000000000000000000000000000FF64DFD2C966A837024098F2CE6E3F0EAEA7FE35FD21FCAFA94EEFDB22");
	// Decrypt the file
	fnameCheck = "excontent.aes128.chk.txt";
	r = dipki::Cipher::FileDecrypt(fnameCheck, fnameEnc, key, dipki::bvec_t(), dipki::Cipher::Alg::Aes128, dipki::Cipher::Mode::CTR, dipki::Cipher::Padding::Default, dipki::Cipher::Opts::PrefixIV);
	cout << "Created file " << fnameCheck << endl;
	bv = read_binary_file(fnameCheck);
	cout << "DT=" << dipki::Cnv::ToHex(bv) << endl;
	cout << "DT='" << dipki::bvec2str(bv) << "'" << endl;
}

void test_Cipher_GCM() {
	// New in [v20.7]
	dipki::bvec_t key, iv, pt, ct, dt;
	std::string fnameData, fnameEnc, fnameCheck;
	dipki::bvec_t bv;
	int r;
	cout << "\nTESTING Cipher GCM..." << endl;
	cout << "Encrypt a file using AES-GCM..." << endl;
	key = dipki::Cnv::FromHex("2B7E151628AED2A6ABF7158809CF4F3C");
	iv = dipki::Cnv::FromHex("000102030405060708090A0B");
	fnameData = "excontent.txt";
	fnameEnc = "excontent.aes128gcm.enc.dat";
	r = dipki::Cipher::FileEncrypt(fnameEnc, fnameData, key, iv, dipki::Cipher::Alg::Aes128, dipki::Cipher::Mode::GCM, dipki::Cipher::Padding::Default, dipki::Cipher::Opts::PrefixIV);
	cout << "Created file " << fnameEnc << endl;
	bv = read_binary_file(fnameEnc);
	cout << "CT=" << dipki::Cnv::ToHex(bv) << endl;
	// Decrypt the file NB IV is included in CT
	fnameCheck = "excontent.aes128gcm.chk.txt";
	r = dipki::Cipher::FileDecrypt(fnameCheck, fnameEnc, key, dipki::bvec_t(), dipki::Cipher::Alg::Aes128, dipki::Cipher::Mode::GCM, dipki::Cipher::Padding::Default, dipki::Cipher::Opts::PrefixIV);
	cout << "Created file " << fnameCheck << endl;
	bv = read_binary_file(fnameCheck);
	cout << "DT=" << dipki::Cnv::ToHex(bv) << endl;
	cout << "DT='" << dipki::bvec2str(bv) << "'" << endl;

	cout << "Encrypt bytes using AES-GCM..." << endl;
	// Same as EncryptAEAD except without AAD
	pt = read_binary_file(fnameData);
	ct = dipki::Cipher::Encrypt(pt, key, iv, "aes128-gcm");
	cout << "CT=" << dipki::Cnv::ToHex(ct) << endl;
	dt = dipki::Cipher::Decrypt(ct, key, iv, "aes128-gcm");
	cout << "DT=" << dipki::Cnv::ToHex(dt) << endl;
	cout << "DT='" << dipki::bvec2str(dt) << "'" << endl;

}

void test_Cipher_Aead() {
	dipki::bvec_t key, iv, pt, ct, dt, aad;
	cout << "\nTESTING Cipher AEAD..." << endl;
	cout << "GCM Test Case #03 (AES-128)" << endl;
	key = dipki::Cnv::FromHex("feffe9928665731c6d6a8f9467308308");
	iv = dipki::Cnv::FromHex("cafebabefacedbaddecaf888");
	pt = dipki::Cnv::FromHex("d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255");
	cout << "KY=" << dipki::Cnv::ToHex(key) << endl;
	cout << "IV=" << dipki::Cnv::ToHex(iv) << endl;
	cout << "PT=" << dipki::Cnv::ToHex(pt) << endl;
	ct = dipki::Cipher::EncryptAEAD(pt, key, iv, dipki::Cipher::AeadAlg::Aes_128_Gcm);
	cout << "CT=" << dipki::Cnv::ToHex(ct) << endl;
	assert(dipki::Cnv::ToHex(ct) == 
		"42831EC2217774244B7221B784D0D49CE3AA212F2C02A4E035C17E2329ACA12E21D514B25466931C7D8F6A5AAC84AA051BA30B396A0AAC973D58E091473F59854D5C2AF327CD64A62CF35ABD2BA6FAB4");
	// Decrypt - must pass IV here
	dt = dipki::Cipher::DecryptAEAD(ct, key, iv, dipki::Cipher::AeadAlg::Aes_128_Gcm);
	cout << "DT=" << dipki::Cnv::ToHex(dt) << endl;
	assert(dipki::Cnv::ToHex(dt) == dipki::Cnv::ToHex(pt));

	// Prepend IV to ciphertext...
	ct = dipki::Cipher::EncryptAEAD(pt, key, iv, dipki::Cipher::AeadAlg::Aes_128_Gcm, dipki::Cipher::Opts::PrefixIV);
	cout << "IV|CT=" << dipki::Cnv::ToHex(ct) << endl;
	assert(dipki::Cnv::ToHex(ct) == "CAFEBABEFACEDBADDECAF888"
		"42831EC2217774244B7221B784D0D49CE3AA212F2C02A4E035C17E2329ACA12E21D514B25466931C7D8F6A5AAC84AA051BA30B396A0AAC973D58E091473F59854D5C2AF327CD64A62CF35ABD2BA6FAB4");
	// Decrypt - IV is prefixed to CT, so pass an empty IV
	dt = dipki::Cipher::DecryptAEAD(ct, key, dipki::bvec_t(), dipki::Cipher::AeadAlg::Aes_128_Gcm, dipki::Cipher::Opts::PrefixIV);
	cout << "DT=" << dipki::Cnv::ToHex(dt) << endl;
	assert(dipki::Cnv::ToHex(dt) == dipki::Cnv::ToHex(pt));

	cout << "GCM Test Case #08 (AES-192)" << endl;
	key = dipki::Cnv::FromHex("000000000000000000000000000000000000000000000000");
	iv = dipki::Cnv::FromHex("000000000000000000000000");
	pt = dipki::Cnv::FromHex("00000000000000000000000000000000");
	cout << "KY=" << dipki::Cnv::ToHex(key) << endl;
	cout << "IV=" << dipki::Cnv::ToHex(iv) << endl;
	cout << "PT=" << dipki::Cnv::ToHex(pt) << endl;
	ct = dipki::Cipher::EncryptAEAD(pt, key, iv, dipki::Cipher::AeadAlg::Aes_192_Gcm);
	cout << "CT=" << dipki::Cnv::ToHex(ct) << endl;
	assert(dipki::Cnv::ToHex(ct) ==
		"98E7247C07F0FE411C267E4384B0F6002FF58D80033927AB8EF4D4587514F0FB");
	// Decrypt - must pass IV here
	dt = dipki::Cipher::DecryptAEAD(ct, key, iv, dipki::Cipher::AeadAlg::Aes_192_Gcm);
	cout << "DT=" << dipki::Cnv::ToHex(dt) << endl;
	assert(dipki::Cnv::ToHex(dt) == dipki::Cnv::ToHex(pt));

	cout << "GCM Test Case #16 (AES-256)" << endl;
	key = dipki::Cnv::FromHex("feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308");
	iv = dipki::Cnv::FromHex("cafebabefacedbaddecaf888");
	pt = dipki::Cnv::FromHex("d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39");
	aad = dipki::Cnv::FromHex("feedfacedeadbeeffeedfacedeadbeefabaddad2");
	cout << "KY=" << dipki::Cnv::ToHex(key) << endl;
	cout << "IV=" << dipki::Cnv::ToHex(iv) << endl;
	cout << "PT=" << dipki::Cnv::ToHex(pt) << endl;
	cout << "AD=" << dipki::Cnv::ToHex(aad) << endl;
	ct = dipki::Cipher::EncryptAEAD(pt, key, iv, dipki::Cipher::AeadAlg::Aes_256_Gcm, dipki::Cipher::Opts::None, aad);
	cout << "CT=" << dipki::Cnv::ToHex(ct) << endl;
	assert(dipki::Cnv::ToHex(ct) ==
		"522DC1F099567D07F47F37A32A84427D643A8CDCBFE5C0C97598A2BD2555D1AA8CB08E48590DBB3DA7B08B1056828838C5F61E6393BA7A0ABCC9F66276FC6ECE0F4E1768CDDF8853BB2D551B");
	// Decrypt - must pass IV here, and the original AAD
	dt = dipki::Cipher::DecryptAEAD(ct, key, iv, dipki::Cipher::AeadAlg::Aes_256_Gcm, dipki::Cipher::Opts::None, aad);
	cout << "DT=" << dipki::Cnv::ToHex(dt) << endl;
	assert(dipki::Cnv::ToHex(dt) == dipki::Cnv::ToHex(pt));

}


void test_Cipher_KeyWrap() {
	dipki::bvec_t wk;
	dipki::bvec_t keydata, kek, unwk;
	cout << "\nTESTING Cipher KeyWrap..." << endl;
	cout << "Using AES-128 Key wrap" << endl;
	keydata = dipki::Cnv::FromHex("00112233 44556677 8899aabb ccddeeff");
	kek = dipki::Cnv::FromHex("c17a44e8 e28d7d64 81d1ddd5 0a3b8914");
	// Wrap key using AES-128 Key Wrap
	wk = dipki::Cipher::KeyWrap(keydata, kek, dipki::Cipher::Alg::Aes128);
	cout << "WK=" << dipki::Cnv::ToHex(wk) << endl;
	cout << "OK=503D75C73630A7B02ECF51B9B29B907749310B77B0B2E054" << endl;
	assert(dipki::Cnv::ToHex(wk) == "503D75C73630A7B02ECF51B9B29B907749310B77B0B2E054");
	// Now unwrap it
	unwk = dipki::Cipher::KeyUnwrap(wk, kek, dipki::Cipher::Alg::Aes128);
	cout << "KY=" << dipki::Cnv::ToHex(unwk) << endl;
	cout << "OK=00112233445566778899AABBCCDDEEFF" << endl;
	assert(dipki::Cnv::ToHex(unwk) == "00112233445566778899AABBCCDDEEFF");

	cout << "Using Triple DES Key Wrap (wrapped key always different value but always 40 bytes)" << endl;
	keydata = dipki::Cnv::FromHex("84e7f2d8 78f89fcc cd2d5eba fc56daf7 3300f27e f771cd68");
	kek = dipki::Cnv::FromHex("8ad8274e 56f46773 8edd83d4 394e5e29 af7c4089 e4f8d9f4");
	wk = dipki::Cipher::KeyWrap(keydata, kek, dipki::Cipher::Alg::Tdea);
	cout << "WK=" << dipki::Cnv::ToHex(wk) << endl;
	assert(wk.size() == 40);	// Triple DES wrapped key is _always_ 40 bytes long.
	// Now unwrap it
	unwk = dipki::Cipher::KeyUnwrap(wk, kek, dipki::Cipher::Alg::Tdea);
	cout << "KY=" << dipki::Cnv::ToHex(unwk) << endl;
	cout << "OK=84E7F2D878F89FCCCD2D5EBAFC56DAF73300F27EF771CD68" << endl;
	assert(dipki::Cnv::ToHex(unwk) == "84E7F2D878F89FCCCD2D5EBAFC56DAF73300F27EF771CD68");
}

void test_Cipher_Pad() {
	dipki::bvec_t block, input, unpad;
	cout << "\nTESTING Cipher Padding..." << endl;
	input = dipki::Cnv::FromHex("FDFDFDFDFD");
	cout << "Input data =  0x" << dipki::Cnv::ToHex(input) << endl;
	block = dipki::Cipher::Pad(input, dipki::Cipher::Alg::Tdea);
	cout << "Padded data = 0x" << dipki::Cnv::ToHex(block) << endl;
	unpad = dipki::Cipher::Unpad(block, dipki::Cipher::Alg::Tdea);
	cout << "Unpadded =    0x" << dipki::Cnv::ToHex(unpad) << endl;

	input = dipki::Cnv::FromHex("FDFD");
	cout << "Input data =  0x" << dipki::Cnv::ToHex(input) << endl;
	block = dipki::Cipher::Pad(input, dipki::Cipher::Alg::Aes128, dipki::Cipher::Padding::OneAndZeroes);
	cout << "Padded data = 0x" << dipki::Cnv::ToHex(block) << endl;
	unpad = dipki::Cipher::Unpad(block, dipki::Cipher::Alg::Aes128, dipki::Cipher::Padding::OneAndZeroes);
	cout << "Unpadded =    0x" << dipki::Cnv::ToHex(unpad) << endl;

	cout << "Invalid padding..." << endl;
	try {
		unpad = dipki::Cipher::Unpad(block, dipki::Cipher::Alg::Tdea);
	}
	catch (std::exception& e) {
		cout << e.what() << endl;
	}


}


void test_Rng() {
	dipki::bvec_t bv;
	cout << "\nTESTING Rng..." << endl;
	cout << "GUID=" << dipki::Rng::Guid() << endl;
	cout << "GUID=" << dipki::Rng::Guid() << endl;
	cout << "GUID=" << dipki::Rng::Guid() << endl;
	cout << "Rng::Number(-5,+5):" << endl;
	for (int i = 0; i < 20; i++) {
		cout << dipki::Rng::Number(-5, 5) << " ";
	}
	cout << endl;
	cout << "Rng::Octet:" << endl;
	for (int i = 0; i < 20; i++) {
		cout << dipki::Rng::Octet() << " ";
	}
	cout << endl;

	cout << "Rng::Bytes:" << endl;
	for (int i = 0; i < 4; i++) {
		bv = dipki::Rng::Bytes(24);
		cout << dipki::Cnv::ToHex(bv) << endl;
	}

	std::string seedfile = "mycppseedfile.dat";
	dipki::Rng::Initialize(seedfile);
	// Show contents of seed file
	bv = read_binary_file(seedfile);
	cout << "Seed file:\n" << dipki::Cnv::ToHex(bv) << endl;
	dipki::Rng::UpdateSeedFile(seedfile);
	bv = read_binary_file(seedfile);
	cout << "New Seed file:\n" << dipki::Cnv::ToHex(bv) << endl;

}

void test_Gen_Errors() {
	cout << "\nTESTING error lookups..." << endl;
	for (int i = 0; i < 5; i++) {
		cout << "Err::ErrorLookup(" << i << ")=" << dipki::Err::ErrorLookup(i) << endl;
	}
	cout << "Err::ErrorLookup(" << -9999 << ")=" << dipki::Err::ErrorLookup(-9999) << endl;
}

void test_Asn1() {
	cout << "\nTESTING Asn1..." << endl;
	cout << dipki::Asn1::TextDumpToString("MAQwAgUA") << endl;
	cout << dipki::Asn1::TextDumpToString("MAQwAgUA", dipki::Asn1::Opts::NoComments) << endl;
	cout << dipki::Asn1::TextDumpToString("MAQwAgUA", dipki::Asn1::Opts::AddLevels) << endl;
	cout << "Asn1::Type(\"AliceRSASignByCarl.cer\")=" << dipki::Asn1::Type("AliceRSASignByCarl.cer") << endl;
	int n = dipki::Asn1::TextDump("AliceRSASignByCarl-asn1-dump.txt", "AliceRSASignByCarl.cer");
	cout << "Asn1::TextDump returns " << n << endl;
	assert(0 == n);
	//cout << read_text_file("AliceRSASignByCarl-asn1-dump.txt") << endl;
}

void test_Hash() {
	cout << "\nTESTING Hash..." << endl;
	std::string hashstr;
	dipki::bvec_t hashbv;
	hashstr = dipki::Hash::HexFromHex("616263");
	cout << "'" << hashstr << "'" << endl;
	hashstr = dipki::Hash::HexFromHex("616263", dipki::Hash::Alg::Sha224);
	cout << "'" << hashstr << "'" << endl;
	hashbv = dipki::Hash::Bytes(dipki::Cnv::FromHex("616263"));
	cout << dipki::Cnv::ToHex(hashbv) << endl;
	hashbv = dipki::Hash::Bytes(dipki::Cnv::FromHex("616263"), dipki::Hash::Alg::Sha224);
	cout << dipki::Cnv::ToHex(hashbv) << endl;
	hashbv = dipki::Hash::Bytes(dipki::Cnv::FromHex("616263"), dipki::Hash::Alg::Sha256);
	cout << dipki::Cnv::ToHex(hashbv) << endl;
	dipki::Hash::Alg myhashalg;
	myhashalg = dipki::Hash::Alg::Sha256;
	hashbv = dipki::Hash::Bytes(dipki::Cnv::FromHex("616263"), myhashalg);
	cout << dipki::Cnv::ToHex(hashbv) << endl;

	hashstr = dipki::Hash::HexFromString("abc");
	cout << "'" << hashstr << "'" << endl;
	hashstr = dipki::Hash::HexFromBytes(dipki::Cnv::FromHex("616263"));
	cout << "'" << hashstr << "'" << endl;
	hashstr = dipki::Hash::HexFromString("abc", myhashalg);
	cout << "'" << hashstr << "'" << endl;
	hashstr = dipki::Hash::HexFromBytes(dipki::Cnv::FromHex("616263"), myhashalg);
	cout << "'" << hashstr << "'" << endl;

	// One-liners
	cout << dipki::Hash::HexFromHex("616263") << endl;
	// a9993e364706816aba3e25717850c26c9cd0d89d
	cout << dipki::Hash::HexFromString("abc", dipki::Hash::Alg::Sha256) << endl;
	// ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
	cout << dipki::Hash::AlgName(dipki::Hash::Alg::Sha512) << endl;
	// sha512
	cout << "AlgName=" << dipki::Hash::AlgName(dipki::Hash::HashAlgFromString("! SHa-256;")) << endl;
	// AlgName=sha256

	hashbv = dipki::Hash::Double(dipki::Cnv::FromHex("616263"), myhashalg);
	cout << "SHA1(SHA1('abc'))=" << dipki::Cnv::ToHex(hashbv) << endl;
	hashbv = dipki::Hash::File("abc.txt", myhashalg);
	cout << dipki::Cnv::ToHex(hashbv) << endl;
	hashstr = dipki::Hash::HexFromFile("abc.txt", myhashalg);
	cout << "'" << hashstr << "'" << endl;

	cout << "File is missing..." << endl;
	try {
		hashbv = dipki::Hash::File("missing.file", myhashalg);
		cout << dipki::Cnv::ToHex(hashbv) << endl;
	}
	catch (std::exception& e) {
		cout << e.what() << endl;
	}

	// Hash the empty string
	cout << "SHA-1 hash the empty string..." << endl;
	hashstr = dipki::Hash::HexFromString("");
	cout << "'" << hashstr << "'" << endl;
	hashbv = dipki::Hash::Bytes(dipki::bvec_t());
	cout << dipki::Cnv::ToHex(hashbv) << endl;
	hashstr = dipki::Hash::HexFromHex("");
	cout << "'" << hashstr << "'" << endl;
	hashbv = dipki::Hash::File("empty.txt");
	cout << dipki::Cnv::ToHex(hashbv) << endl;
	hashstr = dipki::Hash::HexFromFile("empty.txt");
	cout << "'" << hashstr << "'" << endl;


}

void test_Hmac() {
	cout << "\nTESTING Hmac..." << endl;
	std::string hmacstr;
	dipki::bvec_t hmacbv, bv, data, key;
	cout << "Test case 1 from RFC 2202 and RFC 4231" << endl;
	// HEX('Hi There') = 4869205468657265
	hmacstr = dipki::Hmac::HexFromHex("4869205468657265", "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b");
	cout << "HMAC-SHA-1='" << hmacstr << "'" << endl;
	assert(hmacstr == "b617318655057264e28bc0b6fb378c8ef146be00");
	hmacstr = dipki::Hmac::HexFromHex("4869205468657265", "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", dipki::Hmac::Alg::Sha256);
	cout << "HMAC-SHA-256='" << hmacstr << "'" << endl;
	assert(hmacstr == "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7");

	cout << "Test case 4 from RFC 2202 and RFC 4231" << endl;
	key = dipki::Cnv::FromHex("0102030405060708090a0b0c0d0e0f10111213141516171819");
	cout << "key=" << dipki::Cnv::ToHex(key) << endl;
	// data = 0xcd repeated 50 times
	data.clear();
	data.insert(data.begin(), 50, (unsigned char)0xcd);
	cout << "data=" << dipki::Cnv::ToHex(data) << endl;
	bv = dipki::Hmac::Bytes(data, key);
	cout << "HMAC-SHA-1=" << dipki::Cnv::ToHex(bv) << endl;
	assert(bv == dipki::Cnv::FromHex("4c9007f4026250c6bc8414f9bf50c86c2d7235da"));
	bv = dipki::Hmac::Bytes(data, key, dipki::Hmac::Alg::Md5);
	cout << "HMAC-MD5=" << dipki::Cnv::ToHex(bv) << endl;
	assert(bv == dipki::Cnv::FromHex("697eaf0aca3a3aea3a75164746ffaa79"));
	bv = dipki::Hmac::Bytes(data, key, dipki::Hmac::Alg::Sha256);
	cout << "HMAC-SHA-256=" << dipki::Cnv::ToHex(bv) << endl;
	assert(bv == dipki::Cnv::FromHex("82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b"));
	hmacstr = dipki::Hmac::HexFromBytes(data, key, dipki::Hmac::Alg::Sha256);
	cout << "HMAC-SHA-256='" << hmacstr << "'" << endl;
	assert(hmacstr == "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b");
	cout << "Test case 7 from RFC 4231" << endl;
	data = dipki::str2bvec("This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm.");
	// key 0xaa repeated 131 times
	key.clear();
	key.insert(key.begin(), 131, (unsigned char)0xAA);
	bv = dipki::Hmac::Bytes(data, key, dipki::Hmac::Alg::Sha224);
	cout << "HMAC-SHA-224=" << dipki::Cnv::ToHex(bv) << endl;
	assert(bv == dipki::Cnv::FromHex("3a854166ac5d9f023f54d517d0b39dbd946770db9c2b95c9f6f565d1"));

	// One-liners
	cout << dipki::Hmac::HexFromString("Hi There", dipki::Cnv::FromHex("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b")) << endl;
	// b617318655057264e28bc0b6fb378c8ef146be00
	cout << dipki::Hmac::HexFromBytes(dipki::Cnv::FromHex("7768617420646f2079612077616e7420666f72206e6f7468696e673f"), dipki::Cnv::FromHex("4a656665"), dipki::Hmac::Alg::Sha256) << endl;
	// 5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843
	cout << dipki::Hmac::HexFromHex("7768617420646f2079612077616e7420666f72206e6f7468696e673f", "4a656665", dipki::Hmac::Alg::Sha384) << endl;
	// af45d2e376484031617f78d2b58a6b1b9c7ef464f5a01b47e42ec3736322445e8e2240ca5e69e2c78b3239ecfab21649
}

void test_hmac_SHA3() {
	cout << "\nTESTING Hmac(SHA3)..." << endl;
	std::string hmacstr;
	dipki::bvec_t hmacbv, bv, data, key;
	cout << "NIST HMAC_SHA3-256.pdf Sample #1" << endl;
	hmacstr = dipki::Hmac::HexFromBytes(dipki::str2bvec("Sample message for keylen<blocklen"),
		dipki::Cnv::FromHex("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"), dipki::Hmac::Alg::Sha3_256);
	cout << "HMAC-SHA3-256='" << hmacstr << "'" << endl;
	assert(hmacstr == "4fe8e202c4f058e8dddc23d8c34e467343e23555e24fc2f025d598f558f67205");

	cout << "NIST HMAC_SHA3-384.pdf Sample #2" << endl;
	key = dipki::Cnv::FromHex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"
		"202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f"
		"404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f"
		"6061626364656667");
	hmacstr = dipki::Hmac::HexFromBytes(dipki::str2bvec("Sample message for keylen=blocklen"),
		key, dipki::Hmac::Alg::Sha3_384);
	cout << "HMAC-SHA3-384='" << hmacstr << "'" << endl;
	assert(hmacstr == "a27d24b592e8c8cbf6d4ce6fc5bf62d8fc98bf2d486640d9eb8099e24047837f5f3bffbe92dcce90b4ed5b1e7e44fa90");
}

void test_Wipe() {
	dipki::bvec_t bv;
	std::string s;
	cout << "\nTESTING Wipe..." << endl;
	bv = dipki::str2bvec("Some secret data");
	dipki::Wipe::Data(bv);
	cout << "After Wipe::Data='" << dipki::Cnv::ToHex(bv) << "'" << endl;
	s = "More secret data";
	dipki::Wipe::String(s);
	cout << "After Wipe::String='" << s << "'" << endl;

	std::string fname = "todelete.txt";
	write_text_file(fname, "Even more secret stuff\n");
	cout << "FILE: " << fname << endl;
	cout << "[" << read_text_file(fname) << "]" << endl;
	dipki::Wipe::File(fname);
	cout << "After Wipe::File=" << "[" << read_text_file(fname) << "]" << endl;
}

void test_Pbe() {
	cout << "\nTESTING PASSWORD-BASED ENCRYPTION (PBE)..." << endl;
	std::string password = "password";
	dipki::bvec_t salt = dipki::Cnv::FromHex("78 57 8E 5A 5D 63 CB 06");
	int count = 2048;
	dipki::bvec_t dk;
	dk = dipki::Pbe::Kdf2(24, password, salt, count);
	cout << "dk=" << dipki::Cnv::ToHex(dk) << endl;
	assert(dk == dipki::Cnv::FromHex("BFDE6BE94DF7E11DD409BCE20A0255EC327CB936FFE93643"));
	// One-liner
	dipki::bvec_t derived_key = dipki::Pbe::Kdf2(24, "password", dipki::Cnv::FromHex("78578E5A5D63CB06"), 2048);
	cout << "derived_key=" << dipki::Cnv::ToHex(derived_key) << endl;
	// derived_key=BFDE6BE94DF7E11DD409BCE20A0255EC327CB936FFE93643
	// Same params but derive longer key (CAUTION: never use same salt in practice)
	dk = dipki::Pbe::Kdf2(64, password, salt, count);
	cout << "dk=" << dipki::Cnv::ToHex(dk) << endl;
	assert(dk == dipki::Cnv::FromHex("BFDE6BE94DF7E11DD409BCE20A0255EC327CB936FFE93643C4B150DEF77511224479994567F2E9B4E3BD0DF7AEDA3022B1F26051D81505C794F8940C04DF1144"));
	// Use different HMAC algorithm
	dk = dipki::Pbe::Kdf2(24, password, salt, count, dipki::Pbe::PrfAlg::Hmac_Sha256);
	cout << "dk=" << dipki::Cnv::ToHex(dk) << endl;
	assert(dk == dipki::Cnv::FromHex("97B5A91D35AF542324881315C4F849E327C4707D1BC9D322"));
}

void test_Pem() {
	cout << "\nTESTING PEM file encoding..." << endl;
	cout << "Create a PEM-format CERTIFICATE file from binary file..." << endl;
	int r;
	std::string h1, h2;
	std::string binfile = "smallca.cer";
	std::string pemfile = "smallca.pem";
	r = dipki::Pem::FileFromBinFile(pemfile, binfile, "CERTIFICATE");
	assert(0 == r);
	cout << "Pem::FileFromBinFile created file '" << pemfile << "'" << endl;
	cout << read_text_file(pemfile) << endl;

	r = dipki::Pem::FileFromBinFile(pemfile, binfile, "CERTIFICATE", 72, dipki::Pem::Eol::Unix);
	assert(0 == r);
	cout << "Pem::FileFromBinFile created file '" << pemfile << "'" << endl;
	cout << read_text_file(pemfile) << endl;


	cout << "Convert PEM to binary..." << endl;
	std::string newbin = "smallca-copy.bin";
	r = dipki::Pem::FileToBinFile(newbin, pemfile);
	assert(0 == r);
	// Compute SHA-1 hash of both binary files to show they are identical
	h1 = dipki::Hash::HexFromFile(binfile);
	cout << "Hash binfile = " << h1 << endl;
	h2 = dipki::Hash::HexFromFile(newbin);
	cout << "Hash newbin  = " << h2 << endl;

}

void test_Smime() {
	cout << "\nTESTING S/MIME..." << endl;
	int r;
	std::string p7mfile, wrappedfile, extractedp7m, query;

	// Wrap a CMS object
	p7mfile = "cmsalice2bob.p7m";
	wrappedfile = "cmsalice2bob-smime-env.txt";
	r = dipki::Smime::Wrap(wrappedfile, p7mfile);
	assert(r >= 0);
	cout << "Smime::Wrap created file '" << wrappedfile << "'" << endl;
	cout << read_text_file(wrappedfile) << endl;

	// Query the S/MIME wrapped file
	query = "smime-type";
	cout << "Smime::Query('" << query << "')=" << dipki::Smime::Query(wrappedfile, query) << endl;
	query = "content-type";
	cout << "Smime::Query('" << query << "')=" << dipki::Smime::Query(wrappedfile, query) << endl;
	query = "encoding";
	cout << "Smime::Query('" << query << "')=" << dipki::Smime::Query(wrappedfile, query) << endl;

	// Now extract original CMS object from it
	extractedp7m = "cmsalice2bob-extracted.p7m";
	r = dipki::Smime::Extract(extractedp7m, wrappedfile);
	assert(r >= 0);	
	cout << "Smime::Extract created file '" << extractedp7m << "'" << endl;
	//cout << dipki::Asn1::TextDumpToString(extractedp7m) << endl;
	cout << "Extracted file has type: " << dipki::Asn1::Type(extractedp7m) << ". SHA-1=" << dipki::Hash::HexFromFile(extractedp7m) << endl;
	// Compare to original file that was wrapped...
	cout << "Original file has type:  " << dipki::Asn1::Type(p7mfile) << ". SHA-1=" << dipki::Hash::HexFromFile(p7mfile) << endl;

	// Test advanced wrap options - binary content plus add "x-" in subtype
	p7mfile = "cmsalice2bob.p7m";
	wrappedfile = "cmsalice2bob-smime-env-opts.dat";
	r = dipki::Smime::Wrap(wrappedfile, p7mfile, dipki::Smime::Encoding::Binary, dipki::Smime::AdvOpts::AddX);
	assert(r >= 0);
	cout << "Smime::Wrap created file '" << wrappedfile << "'" << endl;
	query = "content-type";
	cout << "Smime::Query('" << query << "')=" << dipki::Smime::Query(wrappedfile, query) << endl;
	query = "encoding";
	cout << "Smime::Query('" << query << "')=" << dipki::Smime::Query(wrappedfile, query) << endl;

}

void test_Pwd(bool doPrompt) {
	if (!doPrompt) return;
	cout << "\nTESTING PWD PROMPT..." << endl;
	std::string pwd = dipki::Pwd::Prompt("Caption here", "Enterada da password");
	cout << "Password='" << pwd << "'" << endl;
}

void test_Pfx() {
	cout << "\nTESTING PFX..." << endl;
	int r, n, ncerts;
	bool isok;
	std::string pfxfile, certfile, keyfile, password, p7file;
	std::string certstr, hash1, hash2;

	pfxfile = "bob_bycarl.pfx";
	r = dipki::Pfx::MakeFile(pfxfile, "BobRSASignByCarl.cer", "BobPrivRSAEncrypt.p8e", "password");
	cout << "Pfx::MakeFile returns " << r << endl;
	assert(r == 0);
	cout << "Created file " << pfxfile << endl;
	cout << "Asn1 type=" << dipki::Asn1::Type(pfxfile) << endl;
	isok = dipki::Pfx::SignatureIsValid(pfxfile, "password");
	cout << "Pfx::SignatureIsValid returns " << bool2str(isok) << endl;
	assert(isok);
	// Wrong password
	isok = dipki::Pfx::SignatureIsValid(pfxfile, "wrong-password");
	cout << "Pfx::SignatureIsValid(/wrong-password/) returns " << bool2str(isok) << endl;
	assert(!isok);

	// Extract the certificate from a PKCS-12 PFX file
	certfile = "alice_fromPFX.cer";
	n = dipki::X509::GetCertFromPFX(certfile, pfxfile, "password");
	cout << "X509::GetCertFromPFX returns " << n << " (expected +ve)." << endl;
	cout << "Created file " << certfile << endl;
	cout << "Asn1 type=" << dipki::Asn1::Type(certfile) << endl;

	// We can read the certificate directly into a string from the PFX file
	certstr = dipki::X509::ReadCertStringFromPFX(pfxfile, "password");
	cout << "Cert as a string:" << endl;
	cout << certstr << endl;

	// Show the file and certstr are equal by comparing the hash of their SerialNumber and 
	hash1 = dipki::X509::HashIssuerAndSN(certfile);
	cout << "X509::HashIssuerAndSN(file)=" << hash1 << endl;
	hash2 = dipki::X509::HashIssuerAndSN(certstr);
	cout << "X509::HashIssuerAndSN(string)=" << hash2 << endl;
	assert(hash1 == hash2);

	// Use Carl's X.509 certificate to create a "certificate-only" pkcs-12 file. This is useful for importing the certificate into Mozilla/Firefox. 
	pfxfile = "carl_nokey.p12";
	r = dipki::Pfx::MakeFile(pfxfile, "CarlRSASelf.cer");
	cout << "Pfx::MakeFile(cert-only) returns " << r << endl;
	assert(r == 0);
	cout << "Created file " << pfxfile << endl;
	cout << "Asn1 type=" << dipki::Asn1::Type(pfxfile) << endl;

	// Make a PFX file with three certificates
	pfxfile = "bob-3certsEnc.pfx";
	password = "password";
	n = dipki::Pfx::MakeFile(pfxfile, "BobRSASignByCarl.cer;CarlRSASelf.cer;AliceRSASignByCarl.cer",
		"BobPrivRSAEncrypt.p8e", password, "Bob's friendly 3-cert ID");
	cout << "Pfx.MakeFile(3-certs) returns " << n << " (expected 0)" << endl;
	cout << "Created file '" << pfxfile << "'" << endl;

	// Extract all the certificate from a PKCS-12 PFX file as a PKCS-7 certificate chain file
	p7file = "p7ChainfromPFX.p7c";
	n = dipki::X509::GetP7ChainFromPFX(p7file, pfxfile, "password");
	cout << "X509.GetP7ChainFromPFX returns " << n << " (expected +ve)." << endl;
	cout << "Created file " << p7file << endl;
	cout << "Asn1 type=" << dipki::Asn1::Type(p7file) << endl;

	// How many certs in it?
	ncerts = dipki::X509::GetCertCountInP7Chain(p7file);
	cout << p7file << " contains " << ncerts << " certificates (expected 3)" << endl;
	assert(3 == ncerts);

	// Extract the second cert in the chain
	certfile = "cert2.cer";
	isok = dipki::X509::GetCertFromP7Chain(certfile, p7file, 2);
	cout << "X509::GetCertFromP7Chain(2) returns " << bool2str(isok) << endl;
	assert(isok);
	cout << "Created file '" << certfile << "'" << endl;
	cout << "subjectName: " << dipki::X509::QueryCert(certfile, "subjectName") << endl;

	cout << "Read all certs in order:" << endl;
	for (int i = 1; i <= ncerts; i++) {
		certstr = dipki::X509::ReadCertStringFromP7Chain(p7file, i);
		cout << "cert" << i << ": subjectName: " << dipki::X509::QueryCert(certstr, "subjectName") << endl;

	}

}

void test_Ocsp()
{
	std::string s;
	std::string fname, issuerCert;
	std::string certFile, snStr;

	cout << "\nOCSP (ONLINE CERTIFICATE STATUS PROTOCOL) TESTS:" << endl;
	// Creates an OCSP request to check our own current code signing certificate file dims.cer. 
	// This was issued by the holder of certificate in the file UTNUSERFirst-Object.cer.
	issuerCert = "UTNUSERFirst-Object.cer";
	certFile = "dims.cer";
	cout << "IssuerFile=" << issuerCert << endl;
	cout << "CertFile=" << certFile << endl;
	s = dipki::Ocsp::MakeRequest(issuerCert, certFile);
	cout << "OCSPRequest=" << s << endl;
	assert(s.length() > 0);

	// Pass a hex serial number instead of filename
	snStr = "#x 00 FB C7 23 22 8C 8C 80 22 D8 85 92 23 DE E7 06 60";
	cout << "Cert SerialNumber=" << snStr << endl;
	s = dipki::Ocsp::MakeRequest(issuerCert, snStr);
	cout << "OCSPRequest=" << s << endl;
	assert(s.length() > 0);

	// Use a different hash algorithm
	snStr = "#x 00 FB C7 23 22 8C 8C 80 22 D8 85 92 23 DE E7 06 60";
	cout << "Cert SerialNumber=" << snStr << endl;
	s = dipki::Ocsp::MakeRequest(issuerCert, snStr, dipki::Ocsp::HashAlg::Sha256);
	cout << "OCSPRequest=" << s << endl;
	assert(s.length() > 0);
	cout << dipki::Asn1::TextDumpToString(s) << endl;

	// We use a response received from ocsp.usertrust.com for our own code signing certificate 
	// (this HAS been signed by our cert's issuer)
	fname = "ocsp_response_ok_dims.dat";
	issuerCert = "UTNUSERFirst-Object.cer";
	cout << "ResponseFile=" << fname << endl;
	cout << "IssuerFile=" << issuerCert << endl;
	s = dipki::Ocsp::ReadResponse(fname, issuerCert);
	cout << "OCSPResponse=" << s << endl;
	assert(s.length() > 0);
}

void test_Compr()
{
	cout << "\nCOMPRESS DATA USING ZLIB COMPRESSION:" << endl;
	dipki::bvec_t data, cdata, udata;
	// Put data into a byte array
	data = dipki::str2bvec("hello, hello, hello. This is a 'hello world' message for the world, repeat, for the world.");
	// Compress it
	cdata = dipki::Compr::Compress(data);
	cout << "Compressed " << data.size() << " bytes to " << cdata.size() << endl;

	// Now decompress it
	udata = dipki::Compr::Uncompress(cdata);
	cout << "Uncompressed length is " << udata.size() << endl;

	// Show data as an ascii string
	cout << "[" << dipki::bvec2str(udata) << "]" << endl;
}

void test_Rsa()
{
	cout << "\nRSA TESTS:" << endl;
	cout << "Creating an RSA key pair..." << endl;
	// Default options
	int r = dipki::Rsa::MakeKeys("rsa1024.pub", "rsa1024.p8e", "password", 1024);
	cout << "Rsa::MakeKeys returns " << r << " (expecting 0)" << endl;
	std::string pubkeyfile, prikeyfile;
	pubkeyfile = "rsa1024.pub";
	prikeyfile = "rsa1024.p8e";
	cout << pubkeyfile << "-->" << dipki::Asn1::Type(pubkeyfile) << endl;
	cout << prikeyfile << "-->" << dipki::Asn1::Type(prikeyfile) << endl;
	std::string prikeystr, pubkeystr;
	prikeystr = dipki::Rsa::ReadPrivateKey(prikeyfile, "password");
	cout << "p8e: " << dipki::Rsa::KeyBits(prikeystr) << " bits, " << dipki::Rsa::KeyBytes(prikeystr) << " bytes" << endl;
	cout << "KeyIsPrivate=" << (dipki::Rsa::KeyIsPrivate(prikeystr) ? "true" : "false") << endl;
	pubkeystr = dipki::Rsa::ReadPublicKey(pubkeyfile);
	cout << "pub: " << dipki::Rsa::KeyBits(pubkeystr) << " bits, " << dipki::Rsa::KeyBytes(pubkeystr) << " bytes" << endl;
	cout << "KeyIsPrivate=" << (dipki::Rsa::KeyIsPrivate(pubkeystr) ? "true" : "false") << endl;
	cout << "KeyMatch=" << (dipki::Rsa::KeyMatch(prikeystr, pubkeystr) ? "true" : "false") << endl;
}

void test_Rsa_Xml()
{
	cout << "\nRSA XML TESTS:" << endl;
	std::string keystr, xmlstr;
	xmlstr = "<RSAKeyValue>"
		"<Modulus>CmZ5HcaYgWjeerd0Gbt/sMABxicQJwB1FClC4ZqNjFH"
		"QU7PjeCod5dxa9OvplGgXARSh3+Z83Jqa9V1lViC7qw==</Modulus>"
		"<Exponent>AQAB</Exponent>"
		"</RSAKeyValue>";
	keystr = dipki::Rsa::FromXMLString(xmlstr);
	cout << keystr << endl;
	cout << "KeyHashCode=0x" << std::hex << dipki::Rsa::KeyHashCode(keystr) << std::dec << endl;
	xmlstr = "<ds:RSAKeyValue>"
		"<ds:Modulus>CmZ5HcaYgWjeerd0Gbt/sMABxicQJwB1FClC4ZqNjFH"
		"QU7PjeCod5dxa9OvplGgXARSh3+Z83Jqa9V1lViC7qw==</ds:Modulus>"
		"<ds:Exponent>AQAB</ds:Exponent>"
		"</ds:RSAKeyValue>";
	keystr = dipki::Rsa::FromXMLString(xmlstr);
	cout << keystr << endl;
	cout << "KeyHashCode=0x" << std::hex << dipki::Rsa::KeyHashCode(keystr) << std::dec << endl;

	cout << "FILE: " << "AlicePrivRSASign.p8e" << endl;
	keystr = dipki::Rsa::ReadPrivateKey("AlicePrivRSASign.p8e", "password");
	cout << "KeyHashCode=0x" << std::hex << dipki::Rsa::KeyHashCode(keystr) << std::dec << endl;
	cout << dipki::Rsa::ToXMLString(keystr) << endl;
	cout << dipki::Rsa::ToXMLString(keystr, dipki::Rsa::XmlOptions::ExcludePrivateParams | dipki::Rsa::XmlOptions::HexBinaryFormat) << endl;
	cout << dipki::Rsa::ToXMLString(keystr, dipki::Rsa::XmlOptions::ExcludePrivateParams, "ds") << endl;
	// Now derive internal private key string from XML
	xmlstr = dipki::Rsa::ToXMLString(keystr);
	keystr = dipki::Rsa::FromXMLString(xmlstr);
	cout << "KeyHashCode=0x" << std::hex << dipki::Rsa::KeyHashCode(keystr) << std::dec << endl;

	// Extract fields in base64 encoding from key string
	cout << "Modulus=" << dipki::Rsa::KeyValue(keystr, "Modulus") << endl;
	cout << "Exponent=" << dipki::Rsa::KeyValue(keystr, "Exponent") << endl;

}

void test_Rsa_SaveKey()
{
	cout << "\nRSA SAVE KEY TESTS:" << endl;
	std::string keystr, fname;
	int r;
	size_t pos;
	std::string s;
	cout << "FILE: " << "AlicePrivRSASign.p8e" << endl;
	keystr = dipki::Rsa::ReadPrivateKey("AlicePrivRSASign.p8e", "password");
	cout << "Private KeyHashCode=0x" << std::hex << dipki::Rsa::KeyHashCode(keystr) << std::dec << endl;

	cout << "Save as an unencrypted private key..." << endl;
	fname = "alice_pri.p8";
	r = dipki::Rsa::SaveKey(fname, keystr);
	cout << "Created file '" << fname << "'" << endl;
	cout << dipki::Asn1::Type(fname) << endl;

	cout << "Save encrypted with stronger encryption and in PEM form..." << endl;
	fname = "alice_pri_ex.p8e";
	r = dipki::Rsa::SaveEncKey(fname, keystr, "password1", dipki::Rsa::PbeScheme::Pbkdf2_Aes256, "prf=hmacWithSHA512;count=6000", dipki::Rsa::Format::PEM);
	cout << "Created file '" << fname << "'" << endl;
	cout << dipki::Asn1::Type(fname) << endl;
	// Get first line of file:
	cout << read_first_line(fname) << endl;
	// Examine the ASN.1 text dump
	std::string dump = dipki::Asn1::TextDumpToString(fname);
	cout << dump << endl;
	pos = dump.find("hmacWithSHA512");
	assert(pos != std::string::npos);
	cout << "Check: found 'hmacWithSHA512' as expected" << endl;
	pos = dump.find("aes256-CBC");
	assert(pos != std::string::npos);
	cout << "Check: found 'aes256-CBC' as expected" << endl;

	cout << "Derive public key from private" << endl;
	std::string pubkeystr = dipki::Rsa::PublicKeyFromPrivate(keystr);
	cout << "Public KeyHashCode=0x" << std::hex << dipki::Rsa::KeyHashCode(pubkeystr) << std::dec << endl;
	cout << "Save this as public key in SSL PEM format..." << endl;
	fname = "alice_ssl.pem";
	r = dipki::Rsa::SaveKey(fname, pubkeystr, dipki::Rsa::Format::SSL);
	cout << "Created file '" << fname << "'" << endl;
	cout << dipki::Asn1::Type(fname) << endl;
	// Get first line of file:
	cout << read_first_line(fname) << endl;

	// Extract a private key file from PFX
	std::string pfxFile = "alice.p12";
	cout << "Processing file: " << pfxFile << endl;
	cout << dipki::Asn1::Type(pfxFile) << endl;
	fname = "alice_from_pfx.p8e";
	r = dipki::Rsa::GetPrivateKeyFromPFX(fname, pfxFile);
	cout << "Created file '" << fname << "'" << endl;
	cout << dipki::Asn1::Type(fname) << endl;

}

void test_Rsa_Encode()
{
	cout << "\nRSA ENCODING TESTS:" << endl;
	dipki::bvec_t message, block, decoded, digest;
	int blocklen = 512 / 8;	// We must specify the number of bytes in the key block
	cout << "Encode message for encryption (different each time)..." << endl;
	message = dipki::Cnv::FromHex("DEADBEEF");
	cout << "message=" << dipki::Cnv::ToHex(message) << endl;
	cout << "default EME-PKCS1-v1_5:" << endl;
	block = dipki::Rsa::EncodeMsgForEncryption(blocklen, message);
	cout << "block=" << dipki::Cnv::ToHex(block) << endl;
	// Decode
	decoded = dipki::Rsa::DecodeMsgForEncryption(block);
	cout << "decoded=" << dipki::Cnv::ToHex(decoded) << endl;
	assert(dipki::Cnv::ToHex(message) == dipki::Cnv::ToHex(decoded));

	cout << "EME-OAEP:" << endl;
	block = dipki::Rsa::EncodeMsgForEncryption(blocklen, message, dipki::Rsa::EME::OAEP);
	cout << "block=" << dipki::Cnv::ToHex(block) << endl;
	// Decode
	decoded = dipki::Rsa::DecodeMsgForEncryption(block, dipki::Rsa::EME::OAEP);
	cout << "decoded=" << dipki::Cnv::ToHex(decoded) << endl;
	assert(dipki::Cnv::ToHex(message) == dipki::Cnv::ToHex(decoded));

	cout << "Encode message for signature..." << endl;
	cout << "EMSA-PKCS1-v1_5 (default SHA-1):" << endl;
	block = dipki::Rsa::EncodeMsgForSignature(blocklen, message);
	cout << "block=" << dipki::Cnv::ToHex(block) << endl;
	decoded = dipki::Rsa::DecodeMsgForSignature(block);
	cout << "decoded=" << dipki::Cnv::ToHex(decoded) << endl;
	cout << "Check  =" << dipki::Hash::HexFromBytes(message) << endl;

	cout << "EMSA-PKCS1-v1_5 (using SHA-256):" << endl;
	block = dipki::Rsa::EncodeMsgForSignature(blocklen, message, dipki::Rsa::HashAlg::Sha256);
	cout << "block=" << dipki::Cnv::ToHex(block) << endl;
	decoded = dipki::Rsa::DecodeMsgForSignature(block);
	cout << "decoded=" << dipki::Cnv::ToHex(decoded) << endl;
	cout << "Check  =" << dipki::Hash::HexFromBytes(message, dipki::Hash::Alg::Sha256) << endl;

	// Encode message digest directly for a Signature
	// SHA-256(0xDEADBEEF)=5f78c33274e43fa9de5659265c1d917e25c03722dcb0b8d27db8d5feaa813953
	digest = dipki::Cnv::FromHex("5f78c33274e43fa9de5659265c1d917e25c03722dcb0b8d27db8d5feaa813953");
	block = dipki::Rsa::EncodeMsgForSignature(blocklen, digest, dipki::Rsa::HashAlg::Sha256, true);
	cout << "block=" << dipki::Cnv::ToHex(block) << endl;
	decoded = dipki::Rsa::DecodeMsgForSignature(block);
	cout << "decoded=" << dipki::Cnv::ToHex(decoded) << endl;
	cout << "Check  =" << dipki::Hash::HexFromBytes(message, dipki::Hash::Alg::Sha256) << endl;
	// Extract the full digestInfo
	decoded = dipki::Rsa::DecodeMsgForSignature(block, true);
	cout << "digestInfo=" << dipki::Cnv::ToHex(decoded) << endl;

}

void test_Rsa_Encrypt()
{
	cout << "\nTEST RSA ENCRYPT:" << endl;
	dipki::bvec_t ct, pt, dt, blk;
	std::string pubkeyfile, prikeyfile;
	std::string pubkeystr, prikeystr;
	int keybytes;

	cout << "Encrypt in two parts: encode then do raw encrypt with public key..." << endl;
	pt = dipki::str2bvec("Hi Bob");	// Note usually we use RSA to encrypt a session key
	pubkeystr = dipki::Rsa::ReadPublicKey("BobRSASignByCarl.cer");
	// We need the length of the RSA key modulus in bytes
	keybytes = dipki::Rsa::KeyBytes(pubkeystr);
	cout << "KEYBYTES=" << keybytes << endl;
	blk = dipki::Rsa::EncodeMsgForEncryption(keybytes, pt);
	cout << "BK=" << dipki::Cnv::ToHex(blk) << endl;
	ct = dipki::Rsa::RawPublic(blk, pubkeystr);
	// NB ciphertext is different each time
	cout << "CT=" << dipki::Cnv::ToHex(ct) << endl;
	cout << "Decrypt in two parts: do raw RSA with private key then decode..." << endl;
	prikeystr = dipki::Rsa::ReadPrivateKey("BobPrivRSAEncrypt.p8e", "password");
	blk = dipki::Rsa::RawPrivate(ct, prikeystr);
	cout << "BK=" << dipki::Cnv::ToHex(blk) << endl;
	dt = dipki::Rsa::DecodeMsgForEncryption(blk);
	cout << "DT=" << dipki::Cnv::ToHex(dt) << endl;
	// In this case we expect plain ASCII text
	cout << "DT='" << dipki::bvec2str(dt) << "'" << endl;

	cout << "Same again using simpler Rsa::Encrypt and Rsa::Decrypt..." << endl;
	cout << "PT=" << dipki::Cnv::ToHex(pt) << endl;
	ct = dipki::Rsa::Encrypt(pt, pubkeystr);
	cout << "CT=" << dipki::Cnv::ToHex(ct) << endl;
	dt = dipki::Rsa::Decrypt(ct, prikeystr);
	cout << "DT=" << dipki::Cnv::ToHex(dt) << endl;
	// In this case we expect plain ASCII text
	cout << "DT='" << dipki::bvec2str(dt) << "'" << endl;


	cout << "RSAES-OAEP Encryption Example 1.1 from `oaep-vect.txt` in `pkcs-1v2-1-vec.zip`" << endl;
	cout << "Rsa::Encrypt" << endl;
	pubkeyfile = "rsa-oaep-1.pub";
	pt = dipki::Cnv::FromHex("6628194e12073db03ba94cda9ef9532397d50dba79b987004afefe34");
	cout << "PT=" << dipki::Cnv::ToHex(pt) << endl;
	// Set seed to fixed value for debugging (don't do this in practice)....
	ct = dipki::Rsa::Encrypt(pt, pubkeyfile, dipki::Rsa::EME::OAEP, dipki::Rsa::HashAlg::Sha1, dipki::Rsa::AdvOpts::Default, "seed=18b776ea21069d69776a33e96bad48e1dda0a5ef");
	cout << "CT=" << dipki::Cnv::ToHex(ct) << endl;
	assert(dipki::Cnv::ToHex(ct) ==
		"354FE67B4A126D5D35FE36C777791A3F7BA13DEF484E2D3908AFF722FAD468FB21696DE95D0BE911C2D3174F8AFCC201035F7B6D8E69402DE5451618C21A535FA9D7BFC5B8DD9FC243F8CF927DB31322D6E881EAA91A996170E657A05A266426D98C88003F8477C1227094A0D9FA1E8C4024309CE1ECCCB5210035D47AC72E8A");
	cout << "Rsa::Decrypt" << endl;
	// Decrypt using private key
	prikeyfile = "rsa-oaep-1.p8";	// unencrypted, no password
	dt = dipki::Rsa::Decrypt(ct, prikeyfile, "", dipki::Rsa::EME::OAEP, dipki::Rsa::HashAlg::Sha1);
	cout << "DT=" << dipki::Cnv::ToHex(dt) << endl;
	assert(dipki::Cnv::ToHex(dt) == dipki::Cnv::ToHex(pt));

}

void test_Rsa_Sign()
{
	cout << "\nTEST RSA SIGN:" << endl;
	dipki::bvec_t message, blk, sig, dig, digvalue;
	std::string pubkeystr, prikeystr;
	int keybytes;
	cout << "Sign in two parts: encode then do raw encrypt with private key..." << endl;
	// See also Sig::Sign for a cleaner way.

	// Read in private key
	prikeystr = dipki::Rsa::ReadPrivateKey("AlicePrivRSASign.p8e", "password");
	message = dipki::str2bvec("abc");
	// We need the length of RSA key modulus in bytes
	keybytes = dipki::Rsa::KeyBytes(prikeystr);
	cout << "KEYBYTES=" << keybytes << endl;
	// 1. Encode the message in a block of the correct size
	//    -- this computes the message digest value automatically
	blk = dipki::Rsa::EncodeMsgForSignature(keybytes, message);
	cout << "BLK=[" << dipki::Cnv::ToHex(blk) << "]" << endl;
	// 2. Encrypt the block using "raw" RSA transform
	sig = dipki::Rsa::RawPrivate(blk, prikeystr);
	cout << "SIG=[" << dipki::Cnv::ToHex(sig) << "]" << endl;

	// Read in public key to "decrypt" the signature
	pubkeystr = dipki::Rsa::ReadPublicKey("AliceRSASignByCarl.cer");
	// 1. Decrypt the signature to a block using "raw" RSA transform
	blk = dipki::Rsa::RawPublic(sig, pubkeystr);
	cout << "BLK=[" << dipki::Cnv::ToHex(blk) << "]" << endl;
	// 2. Decode to extract the digest
	dig = dipki::Rsa::DecodeMsgForSignature(blk);
	cout << "DIG=[" << dipki::Cnv::ToHex(dig) << "]" << endl;
	// Check we got a match: SHA-1('abc') == recovered digest value
	assert(dipki::Hash::Bytes(message) == dig);

	cout << "Do again but start with digest value, and use SHA-256..." << endl;
	digvalue = dipki::Hash::Bytes(dipki::str2bvec("abc"), dipki::Hash::Alg::Sha256);
	cout << "SHA256('abc')=" << dipki::Cnv::ToHex(digvalue) << endl;
	blk = dipki::Rsa::EncodeMsgForSignature(keybytes, digvalue, dipki::Rsa::HashAlg::Sha256, true);
	cout << "BLK=[" << dipki::Cnv::ToHex(blk) << "]" << endl;
	sig = dipki::Rsa::RawPrivate(blk, prikeystr);
	cout << "SIG=[" << dipki::Cnv::ToHex(sig) << "]" << endl;
	// Decrypt and decode
	blk = dipki::Rsa::RawPublic(sig, pubkeystr);
	cout << "BLK=[" << dipki::Cnv::ToHex(blk) << "]" << endl;
	dig = dipki::Rsa::DecodeMsgForSignature(blk);
	cout << "DIG=[" << dipki::Cnv::ToHex(dig) << "]" << endl;
	// Check we got a match: SHA-256('abc') == recovered digest value
	assert(dipki::Hash::Bytes(message, dipki::Hash::Alg::Sha256) == dig);

}


void test_Ecc()
{
	cout << "\nECC TESTS:" << endl;
	cout << "Creating an ECC key pair..." << endl;
	int r;
	std::string pubkeyfile, prikeyfile;
	std::string prikeystr, pubkeystr;
	std::string s, query;

	// All default settings...
	pubkeyfile = "ecc_test.pub";
	prikeyfile = "ecc_test.p8e";
	r = dipki::Ecc::MakeKeys(pubkeyfile, prikeyfile, dipki::Ecc::Curve::Ed25519, "password");
	cout << "Ecc::MakeKeys returns " << r << " (expecting 0)" << endl;
	assert(0 == r);
	cout << pubkeyfile << ":" << endl;
	cout << dipki::Asn1::Type(pubkeyfile) << endl;
	cout << dipki::Asn1::TextDumpToString(pubkeyfile) << endl;
	cout << prikeyfile << ":" << endl;
	cout << dipki::Asn1::Type(prikeyfile) << endl;
	cout << dipki::Asn1::TextDumpToString(prikeyfile) << endl;
	// With specialist options...
	pubkeyfile = "ecc_test-ex-pub.pem";
	prikeyfile = "ecc_test-ex-pri.pem";
	// New in [v20.4] use Brainpool curve
	r = dipki::Ecc::MakeKeys(pubkeyfile, prikeyfile, dipki::Ecc::Curve::BrainpoolP512r1, "password",
		dipki::Ecc::PbeScheme::Pbkdf2_Aes128, "count=3999;prf=hmacWithSha256", dipki::Ecc::Format::PEM);
	cout << "Ecc::MakeKeys returns " << r << " (expecting 0)" << endl;
	assert(0 == r);
	cout << pubkeyfile << ":" << endl;
	cout << dipki::Asn1::Type(pubkeyfile) << endl;
	cout << dipki::Asn1::TextDumpToString(pubkeyfile) << endl;
	cout << prikeyfile << ":" << endl;
	cout << dipki::Asn1::Type(prikeyfile) << endl;
	cout << dipki::Asn1::TextDumpToString(prikeyfile) << endl;

	// Read keys into ephemeral internal key strings (encrypted and different each time)
	prikeystr = dipki::Ecc::ReadPrivateKey(prikeyfile, "password");
	cout << "INT PRI KEYSTR=" << prikeystr << endl;
	pubkeystr = dipki::Ecc::ReadPublicKey(pubkeyfile);
	cout << "INT PUB KEYSTR=" << pubkeystr << endl;
	// Query the key strings
	cout << "Querying the private key '" << prikeyfile << "'" << endl;
	query = "curveName";
	s = dipki::Ecc::QueryKey(prikeystr, query);
	cout << "  " << query << "=" << s << endl;
	query = "keyBits";
	s = dipki::Ecc::QueryKey(prikeystr, query);
	cout << "  " << query << "=" << s << endl;
	query = "isPrivate";
	s = dipki::Ecc::QueryKey(prikeystr, query);
	cout << "  " << query << "=" << s << endl;
	cout << "Querying the public key '" << pubkeyfile << "'" << endl;
	query = "curveName";
	s = dipki::Ecc::QueryKey(pubkeystr, query);
	cout << "  " << query << "=" << s << endl;
	query = "keyBits";
	s = dipki::Ecc::QueryKey(pubkeystr, query);
	cout << "  " << query << "=" << s << endl;
	query = "isPrivate";
	s = dipki::Ecc::QueryKey(pubkeystr, query);
	cout << "  " << query << "=" << s << endl;
	cout << "EXPECTING ERRORS..." << endl;
	try {
		s = s = dipki::Ecc::QueryKey(pubkeystr, "bad-query");
		cout << "  " << query << "=" << s << endl;
	}
	catch (std::exception& e) {
		cout << e.what() << endl;
	}
	try {
		s = s = dipki::Ecc::QueryKey("bad-key-string", query);
		cout << "  " << query << "=" << s << endl;
	}
	catch (std::exception& e) {
		cout << e.what() << endl;
	}
	cout << "...END ERRORS." << endl;

	// Clear private key string
	dipki::Wipe::String(prikeystr);
}

void test_Ecc_ByCurve()
{
	cout << "\nECC CREATE CURVE TESTS:" << endl;
	// Create ECC keys using dta in hex strings - no files required.
	std::string keystr;
	std::string hexKey, b58key;
	dipki::Ecc::Curve cvname;
	int32_t hashcode1, hashcode2;

	cout << "1. A NIST P-192 public key in X9.63 uncompressed format..." << endl;
	hexKey =
		"0496C248BE456192FA1380CCF615D171452F41FF31B92BA733524FD77168DEA4425A3EA8FD79B98DC7AFE83C86DCC39A96";
	cvname = dipki::Ecc::Curve::Prime192v1;
	cout << "KEYHEX: " << hexKey << endl;
	cout << "CURVE:  " << dipki::Ecc::CurveNameAsString(cvname) << endl;
	keystr = dipki::Ecc::ReadKeyByCurve(hexKey, cvname);
	cout << "keyBits=" << dipki::Ecc::QueryKey(keystr, "keyBits") << endl;
	cout << "curveName=" << dipki::Ecc::QueryKey(keystr, "curveName") << endl;
	cout << "isPrivate=" << dipki::Ecc::QueryKey(keystr, "isPrivate") << endl;

	cout << "2. A Bitcoin private key in base58 form..." << endl;
	b58key = "6ACCbmy9qwiFcuVgvxNNwMPfoghobzznWrLs3v7t3RmN";
	cout << "KEYB58: " << b58key << endl;
	cvname = dipki::Ecc::Curve::Secp256k1;
	// Convert key to hex
	hexKey = dipki::Cnv::ToHex(dipki::Cnv::FromBase58(b58key));
	cout << "KEYHEX: " << hexKey << endl;
	cout << "CURVE:  " << dipki::Ecc::CurveNameAsString(cvname) << endl;
	keystr = dipki::Ecc::ReadKeyByCurve(hexKey, cvname);
	cout << "keyBits=" << dipki::Ecc::QueryKey(keystr, "keyBits") << endl;
	cout << "curveName=" << dipki::Ecc::QueryKey(keystr, "curveName") << endl;
	cout << "isPrivate=" << dipki::Ecc::QueryKey(keystr, "isPrivate") << endl;
	// We can compute the public key given the private key
	cout << "publicKey=" << dipki::Ecc::QueryKey(keystr, "publicKey") << endl;

	cout << "3. A X25519 private key..." << endl;
	hexKey =
		"77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a";
	cvname = dipki::Ecc::Curve::X25519;
	cout << "KEYHEX: " << hexKey << endl;
	cout << "CURVE:  " << dipki::Ecc::CurveNameAsString(cvname) << endl;
	keystr = dipki::Ecc::ReadKeyByCurve(hexKey, cvname);
	cout << "keyBits=" << dipki::Ecc::QueryKey(keystr, "keyBits") << endl;
	cout << "curveName=" << dipki::Ecc::QueryKey(keystr, "curveName") << endl;
	cout << "isPrivate=" << dipki::Ecc::QueryKey(keystr, "isPrivate") << endl;
	// We can compute the public key given the private key
	cout << "publicKey=" << dipki::Ecc::QueryKey(keystr, "publicKey") << endl;
	hashcode1 = dipki::Ecc::KeyHashCode(keystr);
	cout << "KeyHashCode=0x" << std::hex << hashcode1 << std::dec << endl;

	cout << "4. X25519 public key (matching previous private key)..." << endl;
	hexKey =
		"8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a";
	cvname = dipki::Ecc::Curve::X25519;
	cout << "KEYHEX: " << hexKey << endl;
	cout << "CURVE:  " << dipki::Ecc::CurveNameAsString(cvname) << endl;
	keystr = dipki::Ecc::ReadKeyByCurve(hexKey, cvname, dipki::Ecc::Publicity::PublicKey);	// NB _must_ specify if public for safe curves
	cout << "keyBits=" << dipki::Ecc::QueryKey(keystr, "keyBits") << endl;
	cout << "curveName=" << dipki::Ecc::QueryKey(keystr, "curveName") << endl;
	cout << "isPrivate=" << dipki::Ecc::QueryKey(keystr, "isPrivate") << endl;
	hashcode2 = dipki::Ecc::KeyHashCode(keystr);
	cout << "KeyHashCode=0x" << std::hex << hashcode2 << std::dec << endl;

	// Hash codes of private and public key should match
	assert(hashcode1 == hashcode2);

	// One-liners used in documentation examples
	cout << dipki::Ecc::CurveNameAsString(dipki::Ecc::Curve::Secp256r1) << endl;
	// Secp256r1
}

void test_Ecc_SaveKey()
{
	cout << "\nECC SAVE KEY TESTS:" << endl;
	// Read and save EC keys.
	std::string keyfile;
	std::string prikeystr, pubkeystr;
	int r;
	// An encrypted private key file we made earlier, saved with default options
	keyfile = "ecc_test.p8e";
	cout << "FILE: " << keyfile << endl;
	cout << dipki::Asn1::Type(keyfile) << endl;
	// Read in key to internal key string
	prikeystr = dipki::Ecc::ReadPrivateKey(keyfile, "password");
	cout << "keyBits=" << dipki::Ecc::QueryKey(prikeystr, "keyBits") << endl;
	cout << "isPrivate=" << dipki::Ecc::QueryKey(prikeystr, "isPrivate") << endl;
	cout << "KeyHashCode=0x" << std::hex << dipki::Ecc::KeyHashCode(prikeystr) << std::dec << endl;

	// Save as an unencrypted file in PKCS#8 format
	keyfile = "ecc_test_unenc.p8";
	r = dipki::Ecc::SaveKey(keyfile, prikeystr, dipki::Ecc::KeyType::Pkcs8PrivateKeyInfo);
	cout << "Created new file '" << keyfile << "'" << endl;
	cout << dipki::Asn1::Type(keyfile) << endl;

	// Save with stronger encryption and in textual PEM form (and using a "stronger" password :-)
	keyfile = "ecc_test_stronger.p8e";
	r = dipki::Ecc::SaveEncKey(keyfile, prikeystr, "password1", dipki::Ecc::PbeScheme::Pbkdf2_Aes128, "count=6000;prf=hmacwithSHA256", dipki::Ecc::Format::PEM);
	cout << "Created new file '" << keyfile << "'" << endl;
	cout << dipki::Asn1::Type(keyfile) << endl;

	// Derive the equivalent public key from the private key
	pubkeystr = dipki::Ecc::PublicKeyFromPrivate(prikeystr);
	cout << "isPrivate=" << dipki::Ecc::QueryKey(pubkeystr, "isPrivate") << endl;
	cout << "KeyHashCode=0x" << std::hex << dipki::Ecc::KeyHashCode(pubkeystr) << std::dec << endl;

	// Save public key to a new file in PEM format
	keyfile = "ecc_test_pub_new.pem";
	r = dipki::Ecc::SaveKey(keyfile, pubkeystr);
	cout << "Created new file '" << keyfile << "'" << endl;
	cout << dipki::Asn1::Type(keyfile) << endl;
}

void test_Ecc_ECDH()
{
	cout << "\nECC DH SHARED SECRET TESTS:" << endl;
	std::string ourkey, theirkey, correcthex;
	dipki::Ecc::Curve curve;
	dipki::bvec_t zz;

	// Ref: CAVS 14.1 ECC CDH Primitive (SP800 - 56A Section 5.7.1.2)
	// Test Information for "testecccdh" ecccdhtestvectors.zip
	curve = dipki::Ecc::Curve::P_256;
	cout << "Test for curve " << dipki::Ecc::CurveNameAsString(curve) << " ..." << endl;
	// Our private key is dUIT:
	ourkey = dipki::Ecc::ReadKeyByCurve("7d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534", curve);
	cout << "Our private key has " << dipki::Ecc::QueryKey(ourkey, "keyBits") << " bits" << endl;
	// Their public key as hex is "04" || QCAVSx || QCAVSy
	theirkey = dipki::Ecc::ReadKeyByCurve("04" "700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287"
		 "db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac", curve);
	cout << "Their public key has " << dipki::Ecc::QueryKey(theirkey, "keyBits") << " bits" << endl;
	// Compute shared secret
	zz = dipki::Ecc::DHSharedSecret(ourkey, theirkey);
	cout << "ZZ=" << dipki::Cnv::ToHex(zz) << endl;
	correcthex = "46FC62106420FF012E54A434FBDD2D25CCC5852060561E68040DD7778997BD7B";
	cout << "OK=" << correcthex << endl;
	assert(correcthex == dipki::Cnv::ToHex(zz));

	// Ref: RFC7748 Section 6.1. X25519 function in an Elliptic Curve Diffie-Hellman (ECDH) protocol.
	std::string alice_prikey = "77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a";
	std::string alice_pubkey = "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a";
	std::string bob_prikey = "5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb";
	std::string bob_pubkey = "de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f";
	correcthex = "4A5D9D5BA4CE2DE1728E3BF480350F25E07E21C947D19E3376F09B3C1E161742";
	// NB Using ReadKeyByCurve with X25519 we _must_ specify if key value represents a public or private key
	curve = dipki::Ecc::Curve::X25519;
	cout << "Test for curve " << dipki::Ecc::CurveNameAsString(curve) << " ..." << endl;
	cout << "1. Alice's private + Bob's public" << endl;
	ourkey = dipki::Ecc::ReadKeyByCurve(alice_prikey, curve, dipki::Ecc::Publicity::PrivateKey);
	theirkey = dipki::Ecc::ReadKeyByCurve(bob_pubkey, curve, dipki::Ecc::Publicity::PublicKey);
	// Compute shared secret
	zz = dipki::Ecc::DHSharedSecret(ourkey, theirkey);
	cout << "ZZ=" << dipki::Cnv::ToHex(zz) << endl;
	assert(correcthex == dipki::Cnv::ToHex(zz));
	cout << "2. Bob's private + Alice's public" << endl;
	ourkey = dipki::Ecc::ReadKeyByCurve(bob_prikey, curve, dipki::Ecc::Publicity::PrivateKey);
	theirkey = dipki::Ecc::ReadKeyByCurve(alice_pubkey, curve, dipki::Ecc::Publicity::PublicKey);
	// Compute shared secret
	zz = dipki::Ecc::DHSharedSecret(ourkey, theirkey);
	cout << "ZZ=" << dipki::Cnv::ToHex(zz) << endl;
	assert(correcthex == dipki::Cnv::ToHex(zz));

}

void test_Sig()
{
	cout << "\nTESTING SIG:" << endl;
	std::string sig, keyFile, password, certFile, dataFile;
	std::string correct;
	dipki::bvec_t data;
	bool isok;

	keyFile = "AlicePrivRSASign.p8e";    // Used to sign data
	certFile = "AliceRSASignByCarl.cer"; // Used to verify signature
	password = "password";  // CAUTION: DO NOT HARD-CODE REAL PASSWORDS

	// Create RSA signatures over the string "abc"
	// -- these are all known values we can check for a given private key
	data = dipki::str2bvec("abc");

	// Default rsa-sha1
	cout << "Alice signs 'abc' using default SHA-1 with RSA" << endl;
	correct = // Alice/'abc'/SHA-1
		"YK1aePtKQDDsVCyJdM0V9VOE6DZVTO3ZoyLV9BNcYmep0glwxU5mUQcLAUTUOETImTIN2Pp4Gffr"
		"xqdxUoczLshnXBNhg7P4ofge+WlBgmcTCnVv27LHHZpmdEbjTg6tnPMb+2b4FvMZ0LfkMKXyiRVTmG4A"
		"NyAmHH6QIsDZ8R8=";
	sig = dipki::Sig::SignData(data, keyFile, password);
	cout << sig << endl;
	assert(sig == correct);
	// Verify the signature we just made
	isok = dipki::Sig::VerifyData(sig, data, certFile);
	cout << "Sig::VerifyData returns " << (isok ? "true" : "false") << endl;
	assert(isok);

	cout << "Alice signs 'abc' using SHA-256 with RSA" << endl;
	correct = // Alice/'abc'/SHA-256
		"tLy6hJadL4w9JI/A/qLCG0Vz1fWPIrPMWD8NGmA5wP7HHlUID54elztUYrpdm9RFeh0RCMJ618dw"
		"BpgIutuZg2qQ2i9uMUYB0DvZvkyeD6MqmtVa4ihgc9SLqhigKeP+KB7voEi7PH3hpEr9Wa3kn4mbPpeD"
		"1VHSzgu/qirjOaA=";
	sig = dipki::Sig::SignData(data, keyFile, password, dipki::Sig::Alg::Rsa_Sha256);
	cout << sig << endl;
	assert(sig == correct);
	// Verify the signature we just made
	isok = dipki::Sig::VerifyData(sig, data, certFile, dipki::Sig::Alg::Rsa_Sha256);
	cout << "Sig::VerifyData returns " << (isok ? "true" : "false") << endl;
	assert(isok);
}

void test_Sig_Ecc()
{
	cout << "\nTESTING SIG ECC:" << endl;
	std::string prikeyfile = "p-256.p8e";
	std::string pubkeyfile = "p-256.pub";
	std::string password = "password";
	std::string sig, keystr;
	dipki::Sig::Alg sigalg = dipki::Sig::Alg::Ecdsa_Sha256;
	int r;
	bool isok;

	// One-liner
	cout << dipki::Sig::AlgName(dipki::Sig::Alg::Rsa_Sha256) << endl;
	// rsa-sha256

	// 1. Make a new 256-bit keypair using P-256 curve (`Prime256v1` is a synonym)
	r = dipki::Ecc::MakeKeys(pubkeyfile, prikeyfile, dipki::Ecc::Curve::Prime256v1, password);
	cout << "Ecc::MakeKeys returns " << r << " (expected 0)" << endl;

	// 1a. Query the keys we just made, just to check
	keystr = dipki::Ecc::ReadPrivateKey(prikeyfile, password);

	cout << "curveName=" << dipki::Ecc::QueryKey(keystr, "curveName") << endl;
	cout << "keyBits=" << dipki::Ecc::QueryKey(keystr, "keyBits") << endl;

	// A. Sign the DATA
	cout << "SigAlgorithm: " << dipki::Sig::AlgName(sigalg) << endl;

	dipki::bvec_t data = dipki::str2bvec("abc");
	sig = dipki::Sig::SignData(data, prikeyfile, password, sigalg);
	// Different each time
	cout << sig << endl;

	// Verify the signature over the DATA
	isok = dipki::Sig::VerifyData(sig, data, pubkeyfile, sigalg);
	cout << "Sig::VerifyData returns " << (isok ? "true" : "false") << endl;
	assert(isok);

	// B. Sign the DIGEST of the data
	// Get the hash digest alg from the signature alg
	dipki::Hash::Alg hashalg = dipki::Hash::HashAlgFromString(dipki::Sig::GetHashNameFromSigAlg(sigalg));
	cout << "Hash::AlgName=" << dipki::Hash::AlgName(hashalg) << endl;

	dipki::bvec_t digest = dipki::Hash::Bytes(dipki::str2bvec("abc"), hashalg);
	cout << "digestValue=" << dipki::Cnv::ToHex(digest) << endl;
	sig = dipki::Sig::SignDigest(digest, prikeyfile, password, sigalg);
	cout << sig << endl;
	// Verify the signature over the DIGEST
	isok = dipki::Sig::VerifyDigest(sig, digest, pubkeyfile, sigalg);
	cout << "Sig::VerifyDigest returns " << (isok ? "true" : "false") << endl;
	assert(isok);
	// Make sure we can verify over the original data
	isok = dipki::Sig::VerifyData(sig, data, pubkeyfile, sigalg);
	cout << "Sig::VerifyData returns " << (isok ? "true" : "false") << endl;
	assert(isok);

	// C. Sign a FILE containing the data
	std::string fname = "abc.dat";
	write_binary_file(fname, dipki::str2bvec("abc"));
	cout << "Created file '" << fname << "'" << endl;
	dipki::bvec_t bv = read_binary_file(fname);
	cout << dipki::Cnv::ToHex(bv) << endl;

	sig = dipki::Sig::SignFile(fname, prikeyfile, password, sigalg);
	cout << sig << endl;
	// Verify the signature over the FILE
	isok = dipki::Sig::VerifyFile(sig, fname, pubkeyfile, sigalg);
	cout << "Sig::VerifyFile returns " << (isok ? "true" : "false") << endl;
	assert(isok);
	// Make sure we can verify over the original data
	isok = dipki::Sig::VerifyData(sig, data, pubkeyfile, sigalg);
	cout << "Sig::VerifyData returns " << (isok ? "true" : "false") << endl;
	assert(isok);

};

void test_Sig_Ecc_det()
{
	cout << "\nTESTING SIG ECC DETERMINISTIC:" << endl;
	std::string sig, intKey, intPubKey, hexKey;
	std::string sigdetok = "0f2141a0ebbc44d2e1af90a50ebcfce5e197b3b7d4de036deb18bc9e1f3d7387500cb99cf5f7c157070a8961e38700b7";
	dipki::Ecc::Curve curve;
	dipki::bvec_t data;
	bool isok;

	// Ref: [RFC6979] "Deterministic Usage of the DSA and ECDSA"
	// A.2.3.  ECDSA, 192 Bits (Prime Field)

	// 1. READ IN PRIVATE KEY IN (HEX,CURVENAME) FORMAT
	hexKey = "6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4";
	curve = dipki::Ecc::Curve::P_192;
	cout << "KEYHEX: " << hexKey << endl;
	cout << "CURVE:  " << dipki::Ecc::CurveNameAsString(curve) << endl;
	intKey = dipki::Ecc::ReadKeyByCurve(hexKey, curve);

	// 2. PREPARE INPUT DATA AS AN ARRAY OF BYTES
	data = dipki::str2bvec("test");

	// 3. SIGN IT USING DETERMINISTIC ALGORITHM FROM [RFC6979]
	//    with output in hex encoding
	//    -- deterministic method always gives the same signature value for same input
	sig = dipki::Sig::SignData(data, intKey, "", dipki::Sig::Alg::Ecdsa_Sha1, dipki::Sig::Encoding::Base16, dipki::Sig::SigOptions::UseDeterministic);
	cout << "SIG(hex): " << sig << endl;
	cout << "SIG(ok):  " << sigdetok << endl;
	assert(sig == sigdetok);

	// 4. VERIFY IT WITH THE PUBLIC KEY
	intPubKey = dipki::Ecc::PublicKeyFromPrivate(intKey);
	isok = dipki::Sig::VerifyData(sig, data, intPubKey, dipki::Sig::Alg::Ecdsa_Sha1);
	cout << "Sig::VerifyData returns " << (isok ? "true" : "false") << endl;
	assert(isok);

	// SIGN IT AGAIN WITH SIG IN ASN1DER FORM AND ENCODE IN "URL_SAFE" BASE64URL
	sig = dipki::Sig::SignData(data, intKey, "", dipki::Sig::Alg::Ecdsa_Sha1, dipki::Sig::Encoding::Base64url, 
		dipki::Sig::SigOptions::UseDeterministic | dipki::Sig::SigOptions::Asn1DERStructure);
	cout << "SIG(base64url): " << sig << endl;
	// VERIFY IT AGAIN (note the encoding is detected automatically by Sig::Verifydata)
	isok = dipki::Sig::VerifyData(sig, data, intPubKey, dipki::Sig::Alg::Ecdsa_Sha1);
	cout << "Sig::VerifyData returns " << (isok ? "true" : "false") << endl;
	assert(isok);

};

void test_Sig_PSS()
{
	cout << "\nSIGN DATA USING RSA-PSS:" << endl;
	std::string sigval, oksig;
	dipki::bvec_t msg;
	bool isok;

	std::string certFile = "AliceRSAPssSignByCarl.cer";
	std::string priKeyFile = "AliceRSAPSS.p8e"; // pkcs8-encrypted 
	std::string mypassword = "password";

	// Input data = three-char ASCII string "abc"
	msg = dipki::str2bvec("abc");
	cout << "MSG: " << dipki::Cnv::ToHex(msg) << endl;

	cout << "Sign using RSA-PSS-SHA256 (different each time)" << endl;
	sigval = dipki::Sig::SignData(msg, priKeyFile, mypassword, dipki::Sig::Alg::Rsa_Pss_Sha256);
	cout << "SIG: " << sigval << endl;
	cout << "Verify using X.509 certificate" << endl;
	//  NOTE We must specify the signature algorithm used to sign it
	isok = dipki::Sig::VerifyData(sigval, msg, certFile, dipki::Sig::Alg::Rsa_Pss_Sha256);
	cout << "Sig::VerifyData returns " << (isok ? "true" : "false") << endl;
	assert(isok);

	cout << "Sign using RSA-PSS-SHA256 with zero-length salt (so signature is deterministic) and MGF1-with-SHA1 (just to be awkward)" << endl;
	// (This should be the same each time)
	sigval = dipki::Sig::SignData(msg, priKeyFile, mypassword, dipki::Sig::Alg::Rsa_Pss_Sha256, dipki::Sig::Encoding::Base64, 
		dipki::Sig::SigOptions::PssSaltLenZero | dipki::Sig::SigOptions::Mgf1Sha1);
	cout << "SIG: " << sigval << endl;
	assert(sigval.length() > 0);
	// Known result
	oksig = "e0eb1ocnqg9raQBcjPYhjI5+l23P8aNWUHSPgaW5mUlfh2py1IjD1c/TDGQoIpQisbaBq0yDB4DSSpW0p9RqeQK33bELI3KM6A83q/5+J76WY/ZCPDSGJRTtovV4jiRSvoqPqcL/bJcQv1j9pqH2tTQmdSgArCYAntnLpBLYR2bbm3Q/1hnrs1T8CttYje+qSPvFAmShxSS8ryIm5POj6p2aXXtdDqo47B46nYeHAVArPUT1CKEXMelZWItlTWEjzqnofLO+nquYIpb7gNBExSfkwxTbBHa88UPu35eEe0AfLaxqEudi7YCAZ6/8cBC4MUXlx8Th6PQ5kPKN+i+ibw==";
	assert(sigval == oksig);

	cout << "Verify using X.509 certificate" << endl;
	// We must specify the signature algorithm used to sign it and must specify MGF1-with-SHA1, since we used that option
	// (however, salt length can be detected automatically from signature value)
	isok = dipki::Sig::VerifyData(sigval, msg, certFile, dipki::Sig::Alg::Rsa_Pss_Sha256, dipki::Sig::VerifyOpts::Mgf1Sha1);
	cout << "Sig::VerifyData returns " << (isok ? "true" : "false") << endl;
	assert(isok);
}

void test_X509_MakeCert()
{
	cout << "\nTEST X509 MAKE CERT:" << endl;
	int r, n;
	bool isok;
	std::string s, certFile, query;
	std::string newCertFile = "myuser.cer";
	std::string issuerCert = "myca.cer";
	std::string subjectPubKeyFile = "mykey.pub";
	std::string issuerPriKeyFile = "myca.p8e";
	std::string password = "password";
	int certNum = 0x101;
	int yearsValid = 4;
	std::string distName = "CN=My User;O=Test Org;OU=Unit;C=AU;L=My Town;S=State;E=myuser@testorg.com";
	std::string email = "myuser@testorg.com";

	r = dipki::X509::MakeCert(newCertFile, issuerCert, subjectPubKeyFile, issuerPriKeyFile, password, certNum, yearsValid, distName, email);
	cout << "X509::MakeCert returns " << r << endl;
	cout << "Created file '" << newCertFile << "'" << endl;
	cout << dipki::Asn1::TextDumpToString(newCertFile) << endl;

	// Make an end-user cert identical to RFC4134 AliceRSASignByCarl.cer 
	subjectPubKeyFile = "AlicePubRSA.pub";
	issuerCert = "CarlRSASelf.cer";
	issuerPriKeyFile = "CarlPrivRSASign.p8e";
	password = "password";
	newCertFile = "AliceRSA-newdup.cer";
	std::string dn = "CN=AliceRSA";
	std::string extns = "rfc822name=AliceRSA@example.com;" 
		"serialNumber=46346BC7800056BC11D36E2EC410B3B0;" 
		"subjectKeyIdentifier=77D2B4D1B74C8A8AA3CE459DCEEC3CA03AE3FF50;" 
		"notBefore=1999-09-19T01:08:47;" 
		"notAfter=2039-12-31;";
	dipki::X509::KeyUsageOptions kuo = dipki::X509::KeyUsageOptions::DigitalSignature | dipki::X509::KeyUsageOptions::NonRepudiation;
	r = dipki::X509::MakeCert(newCertFile, issuerCert, subjectPubKeyFile, issuerPriKeyFile, password, 0, 0, dn, extns, kuo, 
		dipki::X509::SigAlg::Default, dipki::X509::CertOptions::AuthKeyId);
	cout << "X509::MakeCert returns " << r << endl;
	cout << "Created file '" << newCertFile << "'" << endl;
	cout << dipki::Asn1::TextDumpToString(newCertFile) << endl;
	cout << "CERTTHUMB=" << dipki::X509::CertThumb(newCertFile) << endl;
	assert("b30c48855055c2e64ce3196492d4b83831a6b3cb" == dipki::X509::CertThumb(newCertFile));

	certFile = newCertFile;
	cout << "FILE: " << certFile << endl;
	query = "sigAlgId";
	s = dipki::X509::QueryCert(certFile, query);
	cout << "X509::CertQuery(" << query << ")=" << s << endl;
	n = static_cast<int>(dipki::X509::SigAlg::Rsa_Sha1);
	cout << "static_cast<int>(dipki::X509::SigAlg::Rsa_Sha1)=" << n << endl;
	
	query = "signatureAlgorithm";
	s = dipki::X509::QueryCert(certFile, query);
	cout << "X509::CertQuery(" << query << ")=" << s << endl;
	query = "notAfter";
	s = dipki::X509::QueryCert(certFile, query);
	cout << "X509::CertQuery(" << query << ")=" << s << endl;

	// Check that cert is valid now as per system clock (this will fail in 2039!)
	isok = dipki::X509::CertIsValidNow(certFile);
	cout << "X509::CertIsValidNow returns " << bool2str(isok) << endl;
	assert(isok);

	cout << "\nMake a small self-signed X.509 v1 certificate with 386-bit key" << endl;
	// Private key is not encrypted...
	std::string prikeypem = "-----BEGIN PRIVATE KEY-----"
		"MIIBCwIBADANBgkqhkiG9w0BAQEFAASB9jCB8wIBAAIxAvMGwkRKuvMz7WUyRrdc"
		"b975lopHv6/xJWbCHJ52y2Sp8JEdsxWAtWbnZMW5XtxJTwIBAwIxAfdZ1tgx0fd3"
		"85jMLyToSpSmZFwv1R/2FqSFvNj9UDrBBqGzYLDj//YyqiLR6D6QuwIZAcleIuXA"
		"ieV8YHWXdDtfOYtthwwBQfRdOwIZAaabXnM6SScMBij5LdDLe+ot3oV9QIoS/QIZ"
		"ATDpbJkrBpj9laO6TXzqJlzzr11WK/g+JwIZARm86aIm229dWXCmHosyUpwelFj+"
		"Kwa3UwIZAKZZqflnwaG4QAVMqU3SLN9ZcIOpvEt+7w=="
		"-----END PRIVATE KEY-----";
	std::string pubkeypem = "-----BEGIN RSA PUBLIC KEY-----"
		"MDYCMQLzBsJESrrzM+1lMka3XG/e+ZaKR7+v8SVmwhyedstkqfCRHbMVgLVm52TF"
		"uV7cSU8CAQM="
		"-----END RSA PUBLIC KEY-----";
	// Check keys
	cout << "Public key has " << dipki::Rsa::KeyBits(dipki::Rsa::ReadPublicKey(pubkeypem)) << " bits" << endl;
	cout << "Private key has " << dipki::Rsa::KeyBits(dipki::Rsa::ReadPrivateKey(prikeypem)) << " bits" << endl;
	cout << "Rsa::KeyMatch=" << dipki::Rsa::KeyMatch(dipki::Rsa::ReadPrivateKey(prikeypem), dipki::Rsa::ReadPublicKey(pubkeypem)) << endl;
	newCertFile = "tiny.cer";
	r = dipki::X509::MakeCertSelf(newCertFile, prikeypem, "", 2, 0, "CN=A", "notbefore=1999-09-10T01:08:47;notafter=2039-12-31T23:59:59",
		dipki::X509::KeyUsageOptions::NoKeyUsageOption, dipki::X509::SigAlg::Rsa_Sha1, dipki::X509::CertOptions::VersionOne);
	cout << "X509::MakeCert returns " << r << endl;
	cout << "Created file '" << newCertFile << "'" << endl;
	cout << dipki::Asn1::TextDumpToString(newCertFile) << endl;
	cout << "CERTTHUMB=" << dipki::X509::CertThumb(newCertFile) << endl;

	certFile = newCertFile;
	query = "version";
	s = dipki::X509::QueryCert(certFile, query);
	cout << "X509::CertQuery(" << query << ")=" << s << endl;

}

void test_X509_Certs()
{
	cout << "\nTEST X509 CERTS:" << endl;
	std::string s, certFile, query, newFile, thumb;
	int r;

	// A certificate file with Spanish characters in the distinguished name
	certFile = "alicia.cer";
	cout << "\nFILE: " << certFile << endl;

	// Query the issuerName, which contains non-ASCII characters
	query = "issuerName";
	s = dipki::X509::QueryCert(certFile, query);
	cout << "X509::CertQuery(" << query << ", default):\n" << s << endl;
	s = dipki::X509::QueryCert(certFile, query, dipki::X509::OutputOpts::Ldap);
	cout << "X509::CertQuery(" << query << ", Ldap):\n" << s << endl;
	s = dipki::X509::QueryCert(certFile, query, dipki::X509::OutputOpts::UTF8);
	cout << "X509::CertQuery(" << query << ", UTF8):\n" << s << endl;
	s = dipki::X509::QueryCert(certFile, query, dipki::X509::OutputOpts::Latin1);
	cout << "CAUTION: OutputOpts::Latin1 does not work properly on a UTF-8 console!" << endl;
	cout << "X509::CertQuery(" << query << ", Latin1): (NOTE WRONG!)\n" << s << endl;

	query = "serialNumber";
	s = dipki::X509::QueryCert(certFile, query);
	cout << "X509::CertQuery(" << query << "): " << s << endl;
	s = dipki::X509::QueryCert(certFile, query, dipki::X509::OutputOpts::Decimal);
	cout << "X509::CertQuery(" << query << ", Decimal): " << s << endl;

	cout << "TextDump using default, Ldap and UTF8 output options:" << endl;
	cout << dipki::X509::TextDumpToString(certFile) << endl;
	// Truncate these two examples
	cout << dipki::X509::TextDumpToString(certFile, dipki::X509::OutputOpts::Decimal | dipki::X509::OutputOpts::Ldap).substr(0, 300) << "/[cut]...\n" << endl;
	cout << dipki::X509::TextDumpToString(certFile, dipki::X509::OutputOpts::Decimal | dipki::X509::OutputOpts::UTF8).substr(0, 300) << "/[cut]...\n" << endl;

	cout << "Read certfile into a base64 string..." << endl;
	s = dipki::X509::ReadStringFromFile(certFile);
	cout << s << endl;

	newFile = "newcert.pem";
	cout << "Save base64 string to new file '" << newFile << "'..." << endl;
	r = dipki::X509::SaveFileFromString(newFile, s, true);
	cout << read_first_line(newFile) << endl;

	// All three forms should give the same thumbprint
	cout << "Check thumbprints of all three certificate forms above..." << endl;
	thumb = dipki::X509::CertThumb(certFile);
	cout << thumb << endl;
	cout << dipki::X509::CertThumb(s) << endl;
	assert(thumb == dipki::X509::CertThumb(s));
	cout << dipki::X509::CertThumb(newFile) << endl;
	assert(thumb == dipki::X509::CertThumb(newFile));

	// A certificate file signed using RSA-PSS
	certFile = "AliceRSAPssSignByCarl.cer";
	cout << "\nFILE: " << certFile << endl;

	query = "signatureAlgorithm";
	s = dipki::X509::QueryCert(certFile, query);
	cout << "X509::CertQuery(" << query << "): " << s << endl;
	query = "pssParams";
	s = dipki::X509::QueryCert(certFile, query);
	cout << "X509::CertQuery(" << query << "): " << s << endl;

	cout << dipki::X509::TextDumpToString(certFile) << endl;

	cout << "Read certfile into a base64 string..." << endl;
	s = dipki::X509::ReadStringFromFile(certFile);
	cout << s << endl;

	cout << "Check thumbprints of original cert and its base64 string..." << endl;
	thumb = dipki::X509::CertThumb(certFile);
	cout << thumb << endl;
	cout << dipki::X509::CertThumb(s) << endl;
	assert(thumb == dipki::X509::CertThumb(s));
}

void test_X509_CertRequest()
{
	cout << "\nTEST CERT SIGNING REQUEST:" << endl;
	int r;
	bool isok;
	std::string prikeyFile, csrFile;
	// Make a certificate signing request using an RSA private key
	prikeyFile = "AlicePrivRSASign.p8e";
	csrFile = "alice_csr.p10";
	r = dipki::X509::CertRequest(csrFile, prikeyFile, "password", "CN=alice;O=Test Org;C=AU;L=Sydney;S=NSW");
	cout << "X509::CertRequest returns " << r << endl;
	cout << "Created file '" << csrFile << "'" << endl;
	cout << dipki::X509::TextDumpToString(csrFile) << endl;
	// Again using extensions and stronger signature algorithm (yes, we know!)
	csrFile = "alice_csr_ex.p10";
	r = dipki::X509::CertRequest(csrFile, prikeyFile, "password", "CN=alice;O=Test Org;C=AU;L=Sydney;S=NSW",
		"rfc822name=fred@example.com;dnsname=fred.example.com;uri=ftp://ftp.example.com/;ipaddress=192.168.15.1",
		dipki::X509::SigAlg::Rsa_Sha256, dipki::X509::CsrOptions::FormatBinary_Csr);
	cout << "X509::CertRequest returns " << r << endl;
	cout << "Created file '" << csrFile << "'" << endl;
	cout << dipki::X509::TextDumpToString(csrFile) << endl;
	// Use VerifyCert to check the signature in a CSR (we don't need the issuerCert)
	isok = dipki::X509::CertIsVerified(csrFile, "");
	cout << "X509::CertIsVerified returns " << (isok ? "true" : "false") << endl;
	assert(isok);
}

void test_X509_CRL()
{
	cout << "\nTEST CRL (CERTIFICATE REVOCATION LIST):" << endl;
	int r;
	bool isok;
	std::string prikeyFile, crlFile, certFile, issuerCert, password, revokedCertList, extns, dateStr;
	// Carl creates a Certificate Revocation List (CRL)
	// -- A CRL dated with the current system time with default options
	crlFile = "CarlsNew.crl";
	issuerCert = "CarlRSASelf.cer";
	prikeyFile = "CarlPrivRSASign.p8e";
	password = "password";
	revokedCertList = "1,2007-12-31; 2, 2009-12-31T12:59:59Z; 66000,2066-01-01; #x0102deadbeef,2010-02-28T01:01:59";
	r = dipki::X509::MakeCRL(crlFile, issuerCert, prikeyFile, password, revokedCertList);
	cout << "X509::MakeCRL returns " << r << endl;
	cout << "Created file '" << crlFile << "'" << endl;
	cout << dipki::X509::TextDumpToString(crlFile) << endl;
	// -- A CRL using specified times (NB these are GMT times, not local)
	// -- with an empty revocation list and using sha256WithRSAEncryption to sign
	crlFile = "Carl_20211201.crl";
	extns = "thisUpdate=2021-12-01T12:00;nextUpdate=2022-01-01";
	r = dipki::X509::MakeCRL(crlFile, issuerCert, prikeyFile, password, "", extns, dipki::X509::SigAlg::Rsa_Sha256);
	cout << "X509::MakeCRL returns " << r << endl;
	cout << "Created file '" << crlFile << "'" << endl;
	cout << dipki::X509::TextDumpToString(crlFile) << endl;

	// Use VerifyCert to check the signature in a CRL
	isok = dipki::X509::CertIsVerified(crlFile, issuerCert);
	cout << "X509::CertIsVerified returns " << (isok ? "true" : "false") << endl;
	assert(isok);

	// Pass the wrong issuer cert when verifying...
	isok = dipki::X509::CertIsVerified(crlFile, "AliceRSASignByCarl.cer");
	cout << "X509::CertIsVerified(wrong issuer cert) returns " << (isok ? "true" : "false") << " (expecting false)" << endl;
	assert(!isok);

	// Check if a given cert is in a CRL
	// Use test CRL and certs from RFC3280
	crlFile = "rfc3280bis_CRL.crl";
	cout << "CRL File: " << crlFile << endl;
	cout << "This cert has not been revoked..." << endl;
	certFile = "rfc3280bis_cert1.cer";
	cout << "Cert: " << certFile << endl;
	isok = dipki::X509::CertIsRevoked(certFile, crlFile);
	cout << "X509::CertIsRevoked returns " << (isok ? "true" : "false") << endl;
	assert(!isok);
	cout << "This cert HAS been revoked..." << endl;
	certFile = "rfc3280bis_cert2.cer";
	cout << "Cert: " << certFile << endl;
	isok = dipki::X509::CertIsRevoked(certFile, crlFile);
	cout << "X509::CertIsRevoked returns " << (isok ? "true" : "false") << endl;
	assert(isok);
	cout << "But the same cert was not revoked as at 15:00 GMT on 19 November 2004..." << endl;
	dateStr = "2004-11-19T15:00Z";
	isok = dipki::X509::CertIsRevoked(certFile, crlFile, "", dateStr);
	cout << "X509::CertIsRevoked returns " << (isok ? "true" : "false") << " (expecting false)" << endl;
	assert(!isok);


}

void test_CMS_SigData()
{
	cout << "\nTEST CMS SIG DATA:" << endl;
	std::string s, query, fname;

	fname = "4.5.bin";
	cout << "FILE: " << fname << endl;

	query = "version";
	s = dipki::Cms::QuerySigData(fname, query);
	cout << "Cms::QuerySigData(" << query << "): " << s << endl;
	query = "digestAlgorithm";
	s = dipki::Cms::QuerySigData(fname, query);
	cout << "Cms::QuerySigData(" << query << "): " << s << endl;
	query = "signatureValue";
	s = dipki::Cms::QuerySigData(fname, query);
	cout << "Cms::QuerySigData(" << query << "): " << s << endl;

	s = dipki::Cms::GetSigDataDigest(fname);
	cout << "Cms::GetSigDataDigest: " << "[" << s << "]" << endl;
	s = dipki::Cms::GetSigDataDigest("BasicSignByAlice.bin", "AliceRSASignByCarl.cer");
	cout << "Cms::GetSigDataDigest: " << "[" << s << "]" << endl;

}

void test_CMS_EnvData()
{
	cout << "\nTEST CMS ENV DATA:" << endl;
	std::string msg, s, query, fname, fnameCert;
	std::string privateKey;
	int n;
	dipki::bvec_t b;

	// Create an enveloped CMS object Alice to Bob using Bob's X.509 certificate
	fname = "cms2bob_aes128_oaep.p7m";
	fnameCert = "BobRSASignByCarl.cer";
	msg = "This is some sample content.";
	cout << "INPUT MSG: " << msg << endl;
	// This should return 1 (indicating one successful recipient)
	n = dipki::Cms::MakeEnvDataFromString(fname, msg, fnameCert, dipki::Cms::CipherAlg::Aes128, dipki::Cms::KeyEncrAlg::Rsa_Oaep);
	cout << "Cms::MakeEnvDataFromString returns " << n << " (expecting 1)" << endl;
	cout << "FILE: " << fname << endl;

	// Query the enveloped-data file...
	query = "version";
	s = dipki::Cms::QueryEnvData(fname, query);
	cout << "Cms::QueryEnvData(" << query << "): " << s << endl;
	query = "countOfRecipientInfos";
	s = dipki::Cms::QueryEnvData(fname, query);
	cout << "Cms::QueryEnvData(" << query << "): " << s << endl;
	query = "contentEncryptionAlgorithm";
	s = dipki::Cms::QueryEnvData(fname, query);
	cout << "Cms::QueryEnvData(" << query << "): " << s << endl;
	query = "keyEncryptionAlgorithm";
	s = dipki::Cms::QueryEnvData(fname, query);
	cout << "Cms::QueryEnvData(" << query << "): " << s << endl;
	query = "encryptedContent";
	s = dipki::Cms::QueryEnvData(fname, query);
	cout << "Cms::QueryEnvData(" << query << "): " << s << endl;

	// Now try and read it using Bob's private key
	privateKey = dipki::Rsa::ReadPrivateKey("BobPrivRSAEncrypt.p8e", "password");
	cout << "Key length=" << dipki::Rsa::KeyBits(privateKey) << " bits" << endl;
	s = dipki::bvec2str(dipki::Cms::ReadEnvDataToBytes(fname, privateKey));
	cout << "MSG=" << s << endl;

	// As above but passing a byte array instead of a string
	fname = "cms2bob_aes128_oaep_bytes.p7m";
	b = dipki::str2bvec(msg);
	n = dipki::Cms::MakeEnvDataFromBytes(fname, b, fnameCert, dipki::Cms::CipherAlg::Aes128, dipki::Cms::KeyEncrAlg::Rsa_Oaep);
	cout << "FILE: " << fname << endl;
	cout << "Cms::MakeEnvDataFromBytes returns " << n << " (expecting 1)" << endl;
	b = dipki::Cms::ReadEnvDataToBytes(fname, privateKey);
	cout << "MSG=" << dipki::bvec2str(b) << endl;

}

void test_CMS()
{
	cout << "\nCMS (S/MIME OBJECTS) TESTS:" << endl;
	std::string s;
	dipki::bvec_t b;
	int n;
	bool isok;
	std::string msg, fnameInput, fnameOutput, fnameCert, fnameOut1, hexDigest;
	std::string privateKey, certList;

	// Create a test text file
	msg = "This is some sample content.";
	fnameInput = "excontent.txt";
	write_text_file(fnameInput, msg);
	cout << "Input data: '" << read_text_file(fnameInput) << "'" << endl;
	// Create an enveloped CMS object from Alice to Bob using Bob's X.509 certificate
	fnameOutput = "cmsalice2bob.p7m";
	fnameCert = "BobRSASignByCarl.cer";
	// This should return 1 (indicating one successful recipient)
	n = dipki::Cms::MakeEnvData(fnameOutput, fnameInput, fnameCert, dipki::Cms::CipherAlg::Aes128);
	cout << "Cms::MakeEnvData returns " << n << " (expected exactly 1)" << endl;
	assert(n == 1);
	s = dipki::Asn1::Type(fnameOutput);
	cout << "FILE " << fnameOutput << " is a " << s << endl;

	// Now try and read it using Bob's private key, outputting to a file
	fnameOutput = "cmsalice2bob.p7m.txt";
	fnameInput = "cmsalice2bob.p7m";
	privateKey = dipki::Rsa::ReadPrivateKey("BobPrivRSAEncrypt.p8e", "password");
	assert(privateKey.length() > 0);
	n = dipki::Cms::ReadEnvDataToFile(fnameOutput, fnameInput, privateKey);
	cout << "Cms::ReadEnvDataToFile returns " << n << " (expected 0)" << endl;
	assert(n == 0);
	// Read in the resulting text file
	s = read_text_file(fnameOutput);
	cout << "MSG: '" << s << "'" << endl;
	assert(msg == s);

	// and again but read directly into memory as a byte array
	b = dipki::Cms::ReadEnvDataToBytes(fnameInput, privateKey);
	cout << "Cms::ReadEnvDataToBytes returns array of size " << b.size() << " (expected +ve)" << endl;
	assert(b.size() > 0);
	// We know this should be valid text, so convert to a string
	s = dipki::bvec2str(b);
	cout << "MSG: '" << s << "'" << endl;
	assert(msg == s);

	// Generate a BER-encoded CMS signedData object as a file
	// using Alice's private key
	privateKey = dipki::Rsa::ReadPrivateKey("AlicePrivRSASign.p8e", "password");
	fnameOutput = "BasicSignByAlice.bin";
	fnameInput = "excontent.txt";
	fnameCert = "AliceRSASignByCarl.cer";
	n = dipki::Cms::MakeSigData(fnameOutput, fnameInput, fnameCert, privateKey, dipki::Cms::SigAlg::Rsa_Sha256);
	cout << "Cms::MakeSigData returns " << n << " (expected 0)" << endl;
	assert(n == 0);
	s = dipki::Asn1::Type(fnameOutput);
	cout << "FILE " << fnameOutput << " is a " << s << endl;

	// again using a string as input instead of a file
	s = read_text_file(fnameInput);
	fnameOut1 = "BasicSignByAlice1.bin";
	n = dipki::Cms::MakeSigDataFromString(fnameOut1, s, fnameCert, privateKey, dipki::Cms::SigAlg::Rsa_Sha256);
	cout << "Cms::MakeSigDataFromString returns " << n << " (expected 0)" << endl;
	assert(n == 0);
	s = dipki::Asn1::Type(fnameOut1);
	cout << "FILE " << fnameOut1 << " is a " << s << endl;

	// Check we got the same result by comparing the file digests
	assert(dipki::Hash::HexFromFile(fnameOut1) == dipki::Hash::HexFromFile(fnameOutput));

	// Verify the signature directly
	isok = dipki::Cms::VerifySigData(fnameOutput);
	cout << "Cms::VerifySigData returns " << (isok ? "true" : "false") << endl;
	assert(isok);

	// Get message digest of signed-data object eContent
	hexDigest = dipki::Cms::QuerySigData(fnameOutput, "DigestOfeContent");
	cout << "Digest=" << hexDigest << endl;

	// Make a detached signature using the message digest hash
	fnameOutput = "DetSignByAlice.bin";
	n = dipki::Cms::MakeDetachedSig(fnameOutput, hexDigest, fnameCert, privateKey, dipki::Cms::SigAlg::Rsa_Sha256);
	cout << "Cms::MakeDetachedSig returns " << n << " (expected 0)" << endl;
	assert(n == 0);
	s = dipki::Asn1::Type(fnameOutput);
	cout << "FILE " << fnameOutput << " is a " << s << endl;

	// Verify the detached signature using the message digest
	isok = dipki::Cms::VerifySigData(fnameOutput, "", hexDigest);
	cout << "Cms::VerifySigData(detached) returns " << bool2str(isok) << endl;
	assert(isok);

	// Extract the contents from the (first) signed object into another file
	fnameOutput = "excontent_chk.txt";
	fnameInput = "BasicSignByAlice.bin";
	n = dipki::Cms::ReadSigDataToFile(fnameOutput, fnameInput);
	cout << "Cms::ReadSigDataToFile returns " << n << " (expected 28)" << endl;
	assert(n > 0);
	s = read_text_file(fnameOutput);
	cout << "MSG: '" << s << "'" << endl;
	assert(msg == s);

	// Extract the contents directly into memory instead
	b = dipki::Cms::ReadSigDataToBytes(fnameInput);
	cout << "Cms::ReadSigDataToBytes returns array of size " << b.size() << " (expected +ve)" << endl;
	assert(b.size() > 0);
	s = dipki::bvec2str(b);
	cout << "MSG: '" << s << "'" << endl;
	assert(msg == s);

	// Create a "certs-only" SignedData file == certificate chain file
	fnameOutput = "Alice_new.p7c";
	certList = "AliceRSASignByCarl.cer" ";"  "CarlRSASelf.cer";
	n = dipki::Cms::MakeSigData(fnameOutput, "", certList, "", dipki::Cms::SigAlg::Default, dipki::Cms::SigDataOptions::CertsOnly);
	cout << "Cms::MakeSigData(CertsOnly) returns " << n << " (expected 0)" << endl;
	assert(n == 0);
	s = dipki::Asn1::Type(fnameOutput);
	cout << "FILE " << fnameOutput << " is a " << s << endl;

	// How many certs in the chain
	n = dipki::X509::GetCertCountInP7Chain(fnameOutput);
	cout << n << " certificates in the p7 chain" << endl;
	assert(n == 2);
}

void test_CMS_SigData_PSS()
{
	cout << "\nCMS SIG-DATA WITH RSA-PSS:" << endl;
	int r;
	bool isok;
	std::string s;
	std::string privateKey, query;
	std::string prikeyFile = "999009991b_pri.p8e";
	std::string userCert =  "999009991b.cer";
	std::string recipKeyFile =  "999009051b_pri.p8e";
	std::string recipCert =  "999009051b.cer";
	std::string certList = recipCert + ";" +  "CA_4096_Cert.cer" + ";" +  "Int_4096_Cert.cer";
	std::string interFile =  "To_999009051b.int";
	std::string outFile =  "To_999009051b.p7m";
	std::string interFile1 =  "To_999009051b_1.int";
	std::string msg = "Hallo Walt";

	// Read in the sender's private key
	privateKey = dipki::Rsa::ReadPrivateKey(prikeyFile, "password");
	cout << "Key length=" << dipki::Rsa::KeyBits(privateKey) << " bits" << endl;

	cout << "MSG: '" << msg << "'" << endl;
	// Create intermediate signed-data file
	r = dipki::Cms::MakeSigDataFromString(interFile, msg, userCert, privateKey, dipki::Cms::SigAlg::Rsa_Pss_Sha256,
		dipki::Cms::SigDataOptions::IncludeAttributes | dipki::Cms::SigDataOptions::AddSignTime);
	cout << "Created intermediate signed-data file '" << interFile << "'" << endl;
	//cout << dipki::Asn1::TextDumpToString(interFile) << endl;

	// While we're here, let's query the signed-data file
	query = "signatureAlgorithm";
	s = dipki:: Cms::QuerySigData(interFile, query);
	cout << "Cms::QuerySigData(" << query << "): " << s << endl;
	query = "pssParams";
	s = dipki:: Cms::QuerySigData(interFile, query);
	cout << "Cms::QuerySigData(" << query << "): " << s << endl;

	// Encrypt signed-data directly in enveloped-data file: NB returns number of recipients, not zero on success
	r = dipki::Cms::MakeEnvData(outFile, interFile, certList, dipki::Cms::CipherAlg::Aes256, dipki::Cms::KeyEncrAlg::Rsa_Oaep, dipki::Cms::HashAlg::Sha256);
	assert(r > 0);
	cout << "Created enveloped-data file '" << outFile << "'" << endl;
	s = dipki::Asn1::Type(outFile);
	cout << "FILE " << outFile << " is a " << s << endl;

	// And let's query the enveloped-data file
	query = "keyEncryptionAlgorithm";
	s = dipki:: Cms::QueryEnvData(outFile, query);
	cout << "Cms::QueryEnvData(" << query << "): " << s << endl;
	query = "oaepParams";
	s = dipki:: Cms::QueryEnvData(outFile, query);
	cout << "Cms::QueryEnvData(" << query << "): " << s << endl;

	// Clean up
	dipki::Wipe::String(privateKey);

	cout << "\nPart 2: Reading in the enveloped data..." << endl;
	// Read in the recipient's private key (which we shouldn't have, but we do here for testing)
	privateKey = dipki::Rsa::ReadPrivateKey(recipKeyFile, "password");
	cout << "Key length=" << dipki::Rsa::KeyBits(privateKey) << " bits" << endl;

	// Read the enveloped-data file and output the signed-data file
	r = dipki::Cms::ReadEnvDataToFile(interFile1, outFile, privateKey, recipCert);
	assert(r == 0);
	//Console.WriteLine("Read in signed-data file '{0}'", interFile1);
	cout << "Read in signed-data file '" << interFile1 << "'" << endl;

	// Verify the signed data before reading it
	isok = dipki::Cms::VerifySigData(interFile1);
	cout << "Cms::VerifySigData returns " << ::bool2str(isok) << endl;
	assert(isok);

	// Read in the data that was signed (indirectly as bytes then to a string)
	s = dipki::bvec2str(dipki::Cms::ReadSigDataToBytes(interFile1));
	cout << "msg='" << s << "'" << endl;
	assert(s.length() > 0);

	// Clean up
	dipki::Wipe::String(privateKey);

	cout << "Dump certificate data..." << endl;
	s = dipki::X509::TextDumpToString(recipCert, dipki::X509::OutputOpts::Decimal | dipki::X509::OutputOpts::Ldap);
	cout << s << endl;

	isok = dipki::X509::CertPathIsValid(certList);
	cout << "X509::CertPathIsValid returns " << ::bool2str(isok) << endl;
	assert(isok);

}

void test_CMS_SMIME_Chain()
{
	cout << "\nCREATE A CHAIN OF CMS OBJECTS WRAPPED IN S/MIME:" << endl;
	int r;
	bool isok;
	std::string s, inpFile, outFile, query;
	std::string origFile = "sonnets.txt";
	std::string alicekeyfile = "AlicePrivRSASign.p8e";
	std::string alicecerfile = "AliceRSASignByCarl.cer";
	std::string password = "password";
	std::string bobkeyfile = "BobPrivRSAEncrypt.p8e";
	std::string bobcerfile = "BobRSASignByCarl.cer";
	std::string privateKey;
	std::string digest1, digest2;

	// Start with original input file
	inpFile = origFile;
	cout << "File '" << inpFile << "' is " << file_length(inpFile) << " bytes" << endl;

	// Create a CMS compressed-data object, a text file of 106081 bytes
	outFile = "sonnet-compr.p7z";
	r = dipki::Cms::MakeComprData(outFile, inpFile);
	cout << "Cms::MakeComprData returns " << r << " (expected 0)" << endl;
	assert(0 == r);
	inpFile = outFile;
	cout << "File '" << inpFile << "' is " << file_length(inpFile) << " bytes" << endl;

	// Wrap in S/MIME
	outFile = "sonnet-compr-smime.txt";
	r = dipki::Smime::Wrap(outFile, inpFile);
	cout << "Smime::Wrap returns " << r << " (expected +ve)" << endl;
	assert(r > 0);
	inpFile = outFile;
	cout << "File '" << inpFile << "' is " << file_length(inpFile) << " bytes" << endl;

	// Create a CMS signed-data object signed by Alice
	outFile = "sonnet-sig-compr.p7m";
	privateKey = dipki::Rsa::ReadPrivateKey(alicekeyfile, password);
	r = dipki::Cms::MakeSigData(outFile, inpFile, alicecerfile, privateKey);
	cout << "Cms::MakeSigData returns " << r << " (expected 0)" << endl;
	assert(0 == r);
	inpFile = outFile;
	cout << "File '" << inpFile << "' is " << file_length(inpFile) << " bytes" << endl;

	// Wrap in S/MIME
	outFile = "sonnet-sig-compr-smime.txt";
	r = dipki::Smime::Wrap(outFile, inpFile);
	cout << "Smime::Wrap returns " << r << " (expected +ve)" << endl;
	assert(r > 0);
	inpFile = outFile;
	cout << "File '" << inpFile << "' is " << file_length(inpFile) << " bytes" << endl;

	// Create a CMS enveloped-data object encrypted for Bob
	outFile = "sonnet-env-sig-compr.p7m";
	r = dipki::Cms::MakeEnvData(outFile, inpFile, bobcerfile, dipki::Cms::CipherAlg::Aes128);
	cout << "Cms::MakeEnvData returns " << r << " (expected 1)" << endl;
	assert(1 == r);
	inpFile = outFile;
	cout << "File '" << inpFile << "' is " << file_length(inpFile) << " bytes" << endl;

	// Wrap in S/MIME
	outFile = "sonnet-env-sig-compr-smime.txt";
	r = dipki::Smime::Wrap(outFile, inpFile);
	cout << "Smime::Wrap returns " << r << " (expected +ve)" << endl;
	assert(r > 0);
	inpFile = outFile;
	cout << "File '" << inpFile << "' is " << file_length(inpFile) << " bytes" << endl;

	// NOW REVERSE THE PROCEDURE...
	cout << "\nReverse the procedure (and check file types as we go)..." << endl;

	// What S/MIME type are we starting with?
	query = "smime-type";
	s = dipki::Smime::Query(inpFile, query);
	cout << "FILE: " << inpFile << ": " << query << " is '" << s << "'" << endl;

	// Extract from S/MIME
	outFile = "sonnet-out-env-sig-compr.p7m";
	r = dipki::Smime::Extract(outFile, inpFile);
	cout << "Smime::Extract returns " << r << " (expected +ve)" << endl;
	assert(r > 0);
	inpFile = outFile;
	cout << "File '" << inpFile << "' is " << file_length(inpFile) << " bytes" << endl;

	// What ASN.1 type did we get?
	s = dipki::Asn1::Type(inpFile);
	cout << "Found a '" << s << "' file" << endl;

	// Bob decrypts enveloped-file using his private key
	outFile = "sonnet-out-sig-compr-smime.txt";
	privateKey = dipki::Rsa::ReadPrivateKey(bobkeyfile, password);
	assert(privateKey.length() > 0);
	r = dipki::Cms::ReadEnvDataToFile(outFile, inpFile, privateKey);
	cout << "Cms::ReadEnvDataToFile returns " << r << " (expected 0)" << endl;
	assert(r == 0);
	inpFile = outFile;
	cout << "File '" << inpFile << "' is " << file_length(inpFile) << " bytes" << endl;

	// What S/MIME type are we starting with?
	query = "smime-type";
	s = dipki::Smime::Query(inpFile, query);
	cout << "FILE: " << inpFile << ": " << query << " is '" << s << "'" << endl;

	// Extract from S/MIME
	outFile = "sonnet-out-sig-compr.p7m";
	r = dipki::Smime::Extract(outFile, inpFile);
	cout << "Smime::Extract returns " << r << " (expected +ve)" << endl;
	assert(r > 0);
	inpFile = outFile;
	cout << "File '" << inpFile << "' is " << file_length(inpFile) << " bytes" << endl;
	// What ASN.1 type did we get?
	s = dipki::Asn1::Type(inpFile);
	cout << "Found a '" << s << "' file" << endl;

	// Verify the signature in the signed-data object using its embedded certificate
	isok = dipki::Cms::VerifySigData(inpFile);
	cout << "Cms::VerifySigData returns " << (isok ? "true" : "false") << endl;
	assert(isok);

	// Extract contents of signed-data
	outFile = "sonnet-out-compr-smime.txt";
	r = dipki::Cms::ReadSigDataToFile(outFile, inpFile);
	cout << "Cms::ReadSigDataToFile returns " << r << " (expected +ve)" << endl;
	assert(r > 0);
	inpFile = outFile;
	cout << "File '" << inpFile << "' is " << file_length(inpFile) << " bytes" << endl;
	// What S/MIME type are we starting with?
	query = "smime-type";
	s = dipki::Smime::Query(inpFile, query);
	cout << "FILE: " << inpFile << ": " << query << " is '" << s << "'" << endl;

	// Extract from S/MIME
	outFile = "sonnet-out-compr.p7z";
	r = dipki::Smime::Extract(outFile, inpFile);
	cout << "Smime::Extract returns " << r << " (expected +ve)" << endl;
	assert(r > 0);
	inpFile = outFile;
	cout << "File '" << inpFile << "' is " << file_length(inpFile) << " bytes" << endl;
	// What ASN.1 type did we get?
	s = dipki::Asn1::Type(inpFile);
	cout << "Found a '" << s << "' file" << endl;

	// Finally, read contents of CMS compressed-data object
	outFile = "sonnet-out.txt";
	r = dipki::Cms::ReadComprData(outFile, inpFile);
	cout << "Cms::ReadComprData returns " << r << " (expected +ve)" << endl;
	assert(r > 0);
	inpFile = outFile;
	cout << "File '" << inpFile << "' is " << file_length(inpFile) << " bytes" << endl;

	// Check we have no more S/MIME wrapping
	query = "smime-type";
	s = dipki::Smime::Query(inpFile, query);
	cout << "FILE: " << inpFile << ": " << query << " is '" << s << "' (expecting empty)" << endl;
	assert(s.length() == 0);

	// Did we get the same as we started? 
	cout << "Compute SHA-1 digests and compare..." << endl;
	digest1 = dipki::Hash::HexFromFile(origFile, dipki::Hash::Alg::Sha1);
	cout << "SHA1('" << origFile << "')=" << digest1 << endl;
	digest2 = dipki::Hash::HexFromFile(outFile, dipki::Hash::Alg::Sha1);
	cout << "SHA1('" << outFile << "')=" << digest2 << endl;
	assert(digest1 == digest2);

}

void test_CMS_Data_Bytes()
{
	cout << "\nCMS SIGNED DATA USING RAW BYTES:" << endl;
	// We will create a signed-data object with "raw" content as a UTF-8-encoded XML document.

	std::string sigdataFile;
	std::string prikeyFile = "AlicePrivRSASign.p8e";
	std::string certFile = "AliceRSASignByCarl.cer";
	std::string privateKey;
	int r, n;
	std::string query, s;
	dipki::bvec_t contentArr, xmlArr;
	// Create an XML document as a string with lots of obscure characters
	std::wstring wxmlStr = L"\ufeff<?xml version=\"1.0\"?><doc>\n"
		L"<name c='es'>Íñigo</name>\n"
		L"<name c='fr'>Françoise</name>\n"
		L"<name c='pl'>Błażej</name>\n"
		L"<name c='cn'>大卫</name>\n"
		L"</doc>";
	// Does not work at all in basic Windows console!
	//std::wcout << "WIDE: [" << wxmlStr << "]" << L"\r\n";
	// Convert wide string to UTF-8 encoding
	std::string xmlStr = dipki::Cnv::Utf8FromWide(wxmlStr);
	cout << "UTF8: \n" << xmlStr << "]" << endl;
	cout << "String length is " << xmlStr.length() << " bytes" << endl;
	cout << "UTF-8-encoded in hex;" << endl;
	cout << dipki::Cnv::ToHex(dipki::str2bvec(xmlStr)) << endl;

	// Now sign this using Alice's private key
	// (a) passing the UTF-8-encoded string
	sigdataFile = "sigDataByAlice.p7m";
	privateKey = dipki::Rsa::ReadPrivateKey(prikeyFile, "password");
	r = dipki::Cms::MakeSigDataFromString(sigdataFile, xmlStr, certFile, privateKey, dipki::Cms::SigAlg::Rsa_Sha384);
	cout << "Cms::MakeSigDataFromString returns " << r << " (expecting 0)" << endl;
	// (b) passing the same but as raw bytes
	sigdataFile = "sigDataByAlice1.p7m";
	privateKey = dipki::Rsa::ReadPrivateKey(prikeyFile, "password");
	xmlArr = dipki::str2bvec(xmlStr);
	r = dipki::Cms::MakeSigDataFromBytes(sigdataFile, xmlArr, certFile, privateKey, dipki::Cms::SigAlg::Rsa_Sha384);
	cout << "Cms::MakeSigDataFromBytes returns " << r << " (expecting 0)" << endl;

	// signed-data objects with same content and key signed with shaXXXWithRSAEncryption should be identical (not so for PSS or ECDSA or Ed25519)
	assert(dipki::Hash::HexFromFile("sigDataByAlice.p7m") == dipki::Hash::HexFromFile("sigDataByAlice1.p7m"));

	// Extract the signed content to a new byte array
	contentArr = dipki::Cms::ReadSigDataToBytes(sigdataFile);
	cout << "Content length is " << contentArr.size() << " bytes" << endl;
	// We know it's a valid UTF-8 string, so display as a string
	cout << "" << dipki::bvec2str(contentArr) << "" << endl;

	// Extract and save UTF-8-encoded XML to file
	std::string xmlFile = "exampleUTF8.xml";
	r = dipki::Cms::ReadSigDataToFile(xmlFile, sigdataFile);
	cout << "Cms.ReadSigDataToFile returns " << r << " (expecting +ve)" << endl;
	assert(r > 0);
	cout << "Extracted content to file '" << xmlFile << "'" << endl;
	cout << "File length=" << file_length(xmlFile) << endl;
	// Check UTF-8 encoding in file we just made
	n = dipki::Cnv::CheckUTF8File(xmlFile);
	cout << "Cnv::CheckUTF8=" << n << " ==> " << dipki::Cnv::CheckUTF8CodeAsString(n) << endl;

	cout << "\nCMS ENVELOPED DATA USING RAW BYTES:" << endl;
	// Use the same XML data to make an enveloped data file for Bob, Alice and Carl
	// But first we'll make a PKCS#7 certificate chain file and use that to create the env-data
	std::string p7file = "bob_alice_carl.p7b";
	// No need for input file or private key (P7 cert chain is a "degenerate" signed-data file)
	r = dipki::Cms::MakeSigData(p7file, "", "BobRSASignByCarl.cer;AliceRSASignByCarl.cer;CarlRSASelf.cer", "",
		dipki::Cms::SigAlg::Default, dipki::Cms::SigDataOptions::CertsOnly);
	cout << "Cms::MakeSigData returns " << r << "" << endl;
	cout << "Created P7 chain file '" << p7file << "'" << endl;
	cout << "ASN.1 type=" << dipki::Asn1::Type(p7file) << endl;

	std::string envdataFile = "envDataForBobAliceCarl.p7m";
	// Use AES-128 for content encryption and RSA-OAEP with SHA-256 for key transport
	// We can pass a P7 file instead of a list of certificates
	r = dipki::Cms::MakeEnvDataFromBytes(envdataFile, xmlArr, p7file, dipki::Cms::CipherAlg::Aes128,
		dipki::Cms::KeyEncrAlg::Rsa_Oaep, dipki::Cms::HashAlg::Sha256);
	cout << "Cms.MakeEnvDataFromBytes returns " << r << " (= # recipients)" << endl;
	assert(r > 1);
	cout << "Created enveloped-data file '" << envdataFile << "'" << endl;

	// Any of Bob, Alice or Carl can decrypt the message - we'll use Bob's private key
	privateKey = dipki::Rsa::ReadPrivateKey("BobPrivRSAEncrypt.p8e", "password");
	dipki::bvec_t arrMsg = dipki::Cms::ReadEnvDataToBytes(envdataFile, privateKey);
	// Display in hex
	cout << "Received message in hex:" << endl;
	cout << dipki::Cnv::ToHex(arrMsg) << endl;
	cout << "Content length is " << arrMsg.size() << " bytes" << endl;
}

void test_CMS_SigValue()
{
	int r;
	cout << "\nCREATE A SIGNED-DATA CMS OBJECT USING A PRE-COMPUTED SIGNATURE VALUE:" << endl;
	// This example creates an identical SignedData file to example 4.2 from [SMIME-EX]. 
	// In this case, the signature value has been generated separately, perhaps by a smart card with Alice's private key details in it. 
	// The resulting file should be identical to the file 4.2.bin. 

	// Data to be signed in hex format:
	std::string dataHex = "54:68:69:73:20:69:73:20:73:6f:6d:65:20:73:61:6d:70:6c:65:20:63:6f:6e:74:65:6e:74:2e";
	// The signature (generated by the smart card) is:
	std::string sigHex = "2F:23:82:D2:F3:09:5F:B8:0C:58:EB:4E:9D:BF:89:9A"
		"81:E5:75:C4:91:3D:D3:D0:D5:7B:B6:D5:FE:94:A1:8A" 
		"AC:E3:C4:84:F5:CD:60:4E:27:95:F6:CF:00:86:76:75" 
		"3F:2B:F0:E7:D4:02:67:A7:F5:C7:8D:16:04:A5:B3:B5" 
		"E7:D9:32:F0:24:EF:E7:20:44:D5:9F:07:C5:53:24:FA" 
		"CE:01:1D:0F:17:13:A7:2A:95:9D:2B:E4:03:95:14:0B" 
		"E9:39:0D:BA:CE:6E:9C:9E:0C:E8:98:E6:55:13:D4:68" 
		"6F:D0:07:D7:A2:B1:62:4C:E3:8F:AF:FD:E0:D5:5D:C7";
	std::string certFile = "AliceRSASignByCarl.cer";
	std::string cmsFile = "BasicSignByAliceExternal.bin";
	// Convert the hex strings into byte arrays (non-hex chars are stripped)
	dipki::bvec_t data = dipki::Cnv::FromHex(dataHex);
	cout << "MSG=" << dipki::Cnv::ToHex(data) << endl;
	dipki::bvec_t sigval = dipki::Cnv::FromHex(sigHex);
	cout << "SIG=" << dipki::Cnv::ToHex(sigval) << endl;

	// Create the signed-data file
	r = dipki::Cms::MakeSigDataFromSigValue(cmsFile, sigval, data, certFile);
	cout << "Cms::MakeSigDataFromSigValue returns " << r << " (expecting 0)" << endl;
	cout << "Createdfile '" << cmsFile << "'" << endl;
	cout << "ASN.1 type=" << dipki::Asn1::Type(cmsFile) << endl;

	// Check that output file matches `4.2.bin`.
	assert(dipki::Hash::HexFromFile(cmsFile) == dipki::Hash::HexFromFile("4.2.bin"));

}

void test_CMS_Pseudo()
{
	int r;
	bool isok;
	std::string s, dighex, digestValue;

	cout << "\\nCREATE A SIGNED-DATA CMS OBJECT USING 'PSEUDO' PLACEHOLDER:" << endl;
	std::string pseudofile = "BasicSignByAlice_pseudo.p7m";
	r = dipki::Cms::MakeSigData(pseudofile, "excontent.txt", 	/*REQDIN*/
		"AliceRSASignByCarl.cer", 	/*REQDIN*/
		"", /* privkey not required with PSEUDO option */
		dipki::Cms::SigAlg::Rsa_Sha256,
		dipki::Cms::SigDataOptions::PseudoSig | dipki::Cms::SigDataOptions::AltSigAlgId 
		| dipki::Cms::SigDataOptions::IncludeAttributes | dipki::Cms::SigDataOptions::AddSignTime | dipki::Cms::SigDataOptions::AddSigningCertificate);
	cout << "Cms::MakeSigData(PseudoSig) returns " << r << " (expecting 0)" << endl;
	cout << "Createdfile '" << pseudofile << "'" << endl;
	// Expecting bbbbbb... (this is *exactly* the correct length for the final signature)
	cout << "signatureValue: " << dipki::Cms::QuerySigData(pseudofile, "signatureValue") << endl;

	// Check signing time (not required, but just to check, out of interest)
	// NB UTC/GMT time
	cout << "signingTime: " << dipki::Cms::QuerySigData(pseudofile, "signingTime") << endl;

	// Get digest value in hex - this is the digestValue over which the signature will be created.
	dighex = dipki::Cms::QuerySigData(pseudofile, "DigestOfSignedAttrs");
	cout << "DigestOfSignedAttrs: " << dighex << endl;
	// Convert to base64
	digestValue = dipki::Cnv::ToBase64(dipki::Cnv::FromHex(dighex));
	cout << "digestValue: " << digestValue << endl;

	// Pass the digestValue in base64 encoding to the signing agency.
	// They will return the signatureValue (signInfo) in base64 created over the digestValue using "your" private key.
	// User: digestValue --> SigningAgency
	// SigningAgency: signatureValue --> User

	// OK, so we fiddle it here to compute the signatureValue ourselves using Alice's private key...
	std::string signatureValue = dipki::Sig::SignDigest(dipki::Cnv::FromBase64(digestValue), "AlicePrivRSASign.p8e", "password", dipki::Sig::Alg::Rsa_Sha256);
	cout << "signatureValue: " << signatureValue << endl;

	// Now create a new signed-data file from the pseudo file and the received signature Value
	std::string signedFile = "BasicSignByAlice_signed.p7m";
	r = dipki::Cms::MakeSigDataFromPseudo(signedFile, pseudofile, dipki::Cnv::FromBase64(signatureValue));
	cout << "Cms::MakeSigDataFromPseudo returns " << r << " (expecting 0)" << endl;
	cout << "Createdfile '" << signedFile << "'" << endl;

	// Check the resulting file has a valid signature
	isok = dipki::Cms::VerifySigData(signedFile);
	cout << "Cms::VerifySigData returns " << bool2str(isok) << endl;
	assert(isok);

}


void test_ReadJWK()
{
	cout << "\nREAD IN RSA KEY REPRESENTED AS JSON JWK:" << endl;
	// RSA public key as a JSON string
	// Ref: RFC 7517 JSON Web Key (JWK) Appendix A.1
	std::string 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\"}";
	cout << "JSON key=" << json << "" << endl;
	std::string publicKey = dipki::Rsa::ReadPublicKey(json);
	assert(publicKey.length() > 0);
	// Display some key properties
	cout << "RSA key size = " << dipki::Rsa::KeyBits(publicKey) << " bits" << endl;
	cout << "KeyHashCode=0x" << std::hex << dipki::Rsa::KeyHashCode(publicKey) << std::dec << endl;

}

void test_HASH_Length()
{
	cout << "\nTEST HASH LENGTH:" << endl;
	cout << "Hash::Length(Sha256)=" << dipki::Hash::Length(dipki::Hash::Alg::Sha256) << endl;
	cout << "Hash::Length(Btc160)=" << dipki::Hash::Length(dipki::Hash::Alg::Btc160) << endl;
}

void test_KDF_Bytes()
{
	cout << "\nTEST KDF FUNCTION:" << endl;
	int nbytes;
	dipki::bvec_t kek, zz, info;

	// ansx963_2001.rsp CAVS 12.0 'ANS X9.63-2001' information for sample
	nbytes = 128 / 8;
	zz = dipki::Cnv::FromHex("96c05619d56c328ab95fe84b18264b08725b85e33fd34f08");
	kek = dipki::Kdf::Bytes(nbytes, zz, dipki::Kdf::KdfAlg::X963, dipki::Kdf::HashAlg::Sha256);
	cout << "KEK=" << dipki::Cnv::ToHex(kek) << endl;
	assert(dipki::Cnv::ToHex(kek) == "443024C3DAE66B95E6F5670601558F71");

	// [RFC 5869] A.1.  Test Case 1 Basic test case with SHA-256
	nbytes = 42;
	zz = dipki::Cnv::FromHex("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b");
	info = dipki::Cnv::FromHex("f0f1f2f3f4f5f6f7f8f9");
	kek = dipki::Kdf::Bytes(nbytes, zz, dipki::Kdf::KdfAlg::Hkdf, dipki::Kdf::HashAlg::Sha256, info, "salt=000102030405060708090a0b0c");
	cout << "KEK=" << dipki::Cnv::ToHex(kek) << endl;
	assert(dipki::Cnv::ToHex(kek) == "3CB25F25FAACD57A90434F64D0362F2A2D2D0A90CF1A5A4C5DB02D56ECC4C5BF34007208D5B887185865");

	// [RFC 5869] A.3.  Test with SHA-256 and zero-length salt/info
	nbytes = 42;
	zz = dipki::Cnv::FromHex("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b");
	kek = dipki::Kdf::Bytes(nbytes, zz, dipki::Kdf::KdfAlg::Hkdf, dipki::Kdf::HashAlg::Sha256);
	cout << "KEK=" << dipki::Cnv::ToHex(kek) << endl;
	assert(dipki::Cnv::ToHex(kek) == "8DA4E775A563C18F715F802A063C5A31B8A11F5C5EE1879EC3454E5F3C738D2D9D201395FAA4B61A96C8");
}

void test_KDF_ForCms()
{
	cout << "\nTEST KDF FOR CMS:" << endl;
	dipki::bvec_t kek, zz, ukm;

	zz = dipki::Cnv::FromHex("160E3F5588C6FB4E9CEE8BC3C1C5000AB86396468C3D1CAEC0CB6E21536B5513");
	// Use default KDF X963 and SHA-1
	kek = dipki::Kdf::ForCms(zz, dipki::Kdf::KeyWrapAlg::Aes128_wrap);
	cout << "KEK=" << dipki::Cnv::ToHex(kek) << endl;
	assert(dipki::Cnv::ToHex(kek) == "04D616C654CDF62BB186A5A088B60FB5");
	// Use same ZZ but use HKDF, SHA-256, and add user key material
	ukm = dipki::Cnv::FromHex("616263");    // "abc"
	kek = dipki::Kdf::ForCms(zz, dipki::Kdf::KeyWrapAlg::Aes256_wrap, dipki::Kdf::KdfAlg::Hkdf, dipki::Kdf::HashAlg::Sha256, ukm);
	cout << "KEK=" << dipki::Cnv::ToHex(kek) << endl;
	assert(dipki::Cnv::ToHex(kek) == "1D06D6FD5C1EBFB33CAD875E6B99781D3D750875F573C9093CECBFBA6937ACC5");

	cout << "BEGIN EXPECTING ERRORS..." << endl;
	try {
		// Default key wrap is not permitted here
		cout << "Must specify a key wrap algorithm here, not Default..." << endl;
		kek = dipki::Kdf::ForCms(zz, dipki::Kdf::KeyWrapAlg::Default);
		cout << "KEK=" << dipki::Cnv::ToHex(kek) << endl;
	}
	catch (std::exception& e) {
		cout << e.what() << endl;
	}
	cout << "...END EXPECTED ERRORS" << endl;
}

void test_CMS_EnvData_ecdh()
{
	cout << "\nTEST CMS ENVDATA USING ECDH:" << endl;
	int r;
	int nrecips;
	std::string query, certfile;
	std::string prikeyalice, prikeydana;
	dipki::bvec_t bv;
	std::string out;
	std::string msg = "This is some sample content.";

	std::string cmsfile;
	std::string certlist = "lamps-dana.encrypt.crt;lamps-alice.encrypt.crt";
	nrecips = 2;

	// (1) Create an enveloped CMS object to Dana (using ecdh) and Alice (using RSA)
	cmsfile = "dana_alice_all_defaults.p7m";
	r = dipki::Cms::MakeEnvDataFromString(cmsfile, msg, certlist);
	cout << "Cms::MakeEnvDataFromString returns " << r << " (expecting " << nrecips << ")" << endl;
	assert(r == nrecips);
	cout << "FILE: " << cmsfile << endl;
	query = "contentEncryptionAlgorithm";
	cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
	query = "CountOfRecipientInfos";
	cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
	query = "recipientInfoType";
	cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
	query = "keyEncryptionAlgorithm";
	cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
	query = "keyWrapAlgorithm";
	cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
	query = "recipientInfoType/2";
	cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
	query = "keyEncryptionAlgorithm/2";
	cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;

	// Read in Alice's RSA and Dana's ECC private key (both unencrypted)
	prikeyalice = dipki::Rsa::ReadPrivateKey("lamps-alice.decrypt.p8.pem", "");
	assert(prikeyalice.length() > 0);
	prikeydana = dipki::Ecc::ReadPrivateKey("lamps-dana.decrypt.p8.pem", "");
	assert(prikeydana.length() > 0);

	// Read CMS enveloped-data using Alice's RSA key
	certfile = "lamps-alice.encrypt.crt";
	bv = dipki::Cms::ReadEnvDataToBytes(cmsfile, prikeyalice, certfile);
	assert(msg.length() > 0);
	cout << "MSG=" << dipki::bvec2str(bv) << endl;

	// Read CMS enveloped-data using Dana's ECC key
	certfile = "lamps-dana.encrypt.crt";
	bv = dipki::Cms::ReadEnvDataToBytes(cmsfile, prikeydana, certfile);
	assert(msg.length() > 0);
	cout << "MSG=" << dipki::bvec2str(bv) << endl;

	// (2) Create enveloped CMS object using RSA-OAEP but defaults for ECDH plus SHA-256
	cmsfile = "dana_alice_oaep_ecc_defaults.p7m";
	r = dipki::Cms::MakeEnvDataFromString(cmsfile, msg, certlist, dipki::Cms::CipherAlg::Aes128, dipki::Cms::KeyEncrAlg::Rsa_Oaep, dipki::Cms::HashAlg::Sha256, dipki::Cms::EnvDataOptions::Oaep_Mgf1Sha1);
	cout << "Cms::MakeEnvDataFromString returns " << r << " (expecting " << nrecips << ")" << endl;
	assert(r == nrecips);
	cout << "FILE: " << cmsfile << endl;
	query = "contentEncryptionAlgorithm";
	cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
	query = "CountOfRecipientInfos";
	cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
	query = "recipientInfoType";
	cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
	query = "keyEncryptionAlgorithm";
	cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
	query = "recipientInfoType/2";
	cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
	query = "keyEncryptionAlgorithm/2";
	cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
	query = "oaepParams/2";
	cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;

	// Read CMS enveloped-data using Alice's RSA key
	certfile = "lamps-alice.encrypt.crt";
	bv = dipki::Cms::ReadEnvDataToBytes(cmsfile, prikeyalice, certfile);
	assert(msg.length() > 0);
	cout << "MSG=" << dipki::bvec2str(bv) << endl;
	// Read CMS enveloped-data using Dana's ECC key
	certfile = "lamps-dana.encrypt.crt";
	bv = dipki::Cms::ReadEnvDataToBytes(cmsfile, prikeydana, certfile);
	assert(msg.length() > 0);
	cout << "MSG=" << dipki::bvec2str(bv) << endl;


	// (3) Use specific ECDH parameters
	cmsfile = "dana_hkdf.p7m";
	certlist = "lamps-dana.encrypt.crt";
	nrecips = 1;
	r = dipki::Cms::MakeEnvDataFromString(cmsfile, msg, certlist, dipki::Cms::CipherAlg::Aes256, dipki::Cms::KeyEncrAlg::Default, dipki::Cms::HashAlg::Sha384,
		dipki::Cms::EnvDataOptions::Default_EnvDataOpt, dipki::Cms::Format::Default, dipki::Kdf::KdfAlg::Hkdf, dipki::Kdf::KeyWrapAlg::Aes256_wrap, "#xdeadbeef");
	cout << "Cms::MakeEnvDataFromString returns " << r << " (expecting " << nrecips << ")" << endl;
	assert(r == nrecips);

	cout << "FILE: " << cmsfile << endl;
	//cout << dipki::Asn1::TextDumpToString(cmsfile);

	query = "contentEncryptionAlgorithm";
	cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
	query = "CountOfRecipientInfos";
	cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
	query = "recipientInfoType";
	cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
	query = "keyEncryptionAlgorithm";
	cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
	query = "keyWrapAlgorithm";
	cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;

	// Read CMS enveloped-data using Dana's ECC key
	certfile = "lamps-dana.encrypt.crt";
	bv = dipki::Cms::ReadEnvDataToBytes(cmsfile, prikeydana, certfile);
	assert(msg.length() > 0);
	cout << "MSG=" << dipki::bvec2str(bv) << endl;
}

void test_CMS_EnvData_adv()
{
	cout << "\nTEST CMS ENVDATA USING BASIC AND ADVANCED OPTIONS:" << endl;
	int n;
	std::string cmsfile, query;

	// Create an enveloped CMS object (ktri type) to Bob using Bob's RSA key
	n = dipki::Cms::MakeEnvData("cms2bob_aes128.p7m", "excontent.txt", "BobRSASignByCarl.cer", dipki::Cms::CipherAlg::Aes128, dipki::Cms::KeyEncrAlg::Rsa_Oaep);
	cout << "Cms::MakeEnvData returns " << n << " (expecting 1)" << endl;
	assert(1 == n);
	cmsfile = "cms2bob_aes128.p7m";
	cout << "FILE:" << cmsfile << endl;
	cout << dipki::Asn1::Type(cmsfile) << endl;
	query = "recipientInfoType";
	cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
	query = "contentEncryptionAlgorithm";
	cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;

	// Same but using authenticated encryption and creating an authEnvelopedData object
	n = dipki::Cms::MakeEnvData("cms2bob_aes128auth.p7m", "excontent.txt", "BobRSASignByCarl.cer", dipki::Cms::CipherAlg::Aes128, dipki::Cms::KeyEncrAlg::Rsa_Oaep, dipki::Cms::HashAlg::Default, dipki::Cms::EnvDataOptions::Authenticated);
	cout << "Cms::MakeEnvData returns " << n << " (expecting 1)" << endl;
	assert(1 == n);
	cmsfile = "cms2bob_aes128auth.p7m";
	cout << "FILE:" << cmsfile << endl;
	cout << dipki::Asn1::Type(cmsfile) << endl;
	query = "recipientInfoType";
	cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
	query = "contentEncryptionAlgorithm";
	cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;

	// Create an enveloped CMS object (kari type) to Dana using Dana's ECC key
	n = dipki::Cms::MakeEnvData("cms2dana_hkdf.p7m", "excontent.txt", "lamps-dana.encrypt.crt", dipki::Cms::CipherAlg::Aes256, dipki::Cms::KeyEncrAlg::Default, dipki::Cms::HashAlg::Sha384,
		dipki::Cms::EnvDataOptions::Default_EnvDataOpt, dipki::Cms::Format::Default, false, dipki::Kdf::KdfAlg::Hkdf, dipki::Kdf::KeyWrapAlg::Aes256_wrap, "#xdeadbeef");
	cout << "Cms::MakeEnvData returns " << n << " (expecting 1)" << endl;
	assert(1 == n);
	cmsfile = "cms2dana_hkdf.p7m";
	cout << "FILE:" << cmsfile << endl;
	query = "recipientInfoType";
	cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;

	// Create an enveloped CMS object (kekri type) using a previously distributed symmetric key-encryption key (KEK)
	n = dipki::Cms::MakeEnvData("cms_envdata_kekri.p7m", "excontent.txt", "type=@kekri,keyid=ourcommonkey", "#x0123456789ABCDEFF0E1D2C3B4A59687", dipki::Cms::CipherAlg::Aes256, dipki::Kdf::KeyWrapAlg::Aes128_wrap,
		dipki::Cms::EnvDataOptions::Default_EnvDataOpt, dipki::Cms::Format::Default, dipki::Cms::HashAlg::Sha256);
	cout << "Cms::MakeEnvData returns " << n << " (expecting 1)" << endl;
	assert(1 == n);
	cmsfile = "cms_envdata_kekri.p7m";
	cout << "FILE:" << cmsfile << endl;
	query = "recipientInfoType";
	cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
	query = "keyid";
	cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;
	//TODO: make this work
	query = "keyWrapAlgorithm";
	cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;

	// Create an enveloped CMS object (pwri type) using password-based key management
	n = dipki::Cms::MakeEnvData("cms_envdata_pwri.p7m", "excontent.txt", "type=@pwri", "password12345", dipki::Cms::CipherAlg::Aes192, dipki::Kdf::KeyWrapAlg::Default);
	cout << "Cms::MakeEnvData returns " << n << " (expecting 1)" << endl;
	assert(1 == n);
	cmsfile = "cms_envdata_pwri.p7m";
	cout << "FILE:" << cmsfile << endl;
	query = "recipientInfoType";
	cout << "QueryEnvData('" << query << "')=" << dipki::Cms::QueryEnvData(cmsfile, query) << endl;

	cout << "\nNow read in the enveloped-data objects we made above..." << endl;
	std::string prikeystr;
	dipki::bvec_t bv;
	prikeystr = dipki::Rsa::ReadPrivateKey("BobPrivRSAEncrypt.p8e", "password");
	cmsfile = "cms2bob_aes128.p7m";
	cout << "FILE:" << cmsfile << endl;
	bv = dipki::Cms::ReadEnvDataToBytes(cmsfile, prikeystr);
	assert(bv.size() > 0);
	cout << "MSG='" << dipki::bvec2str(bv) << "'" << endl;
	cmsfile = "cms2bob_aes128auth.p7m";
	cout << "FILE:" << cmsfile << endl;
	bv = dipki::Cms::ReadEnvDataToBytes(cmsfile, prikeystr);
	assert(bv.size() > 0);
	cout << "MSG='" << dipki::bvec2str(bv) << "'" << endl;
	prikeystr = dipki::Ecc::ReadPrivateKey("lamps-dana.decrypt.p8.pem", "");
	cmsfile = "cms2dana_hkdf.p7m";
	cout << "FILE:" << cmsfile << endl;
	bv = dipki::Cms::ReadEnvDataToBytes(cmsfile, prikeystr);
	assert(bv.size() > 0);
	cout << "MSG='" << dipki::bvec2str(bv) << "'" << endl;
	cmsfile = "cms_envdata_kekri.p7m";
	cout << "FILE:" << cmsfile << endl;
	bv = dipki::Cms::ReadEnvDataToBytes(cmsfile, "#x0123456789ABCDEFF0E1D2C3B4A59687");
	assert(bv.size() > 0);
	cout << "MSG='" << dipki::bvec2str(bv) << "'" << endl;
	cmsfile = "cms_envdata_pwri.p7m";
	cout << "FILE:" << cmsfile << endl;
	bv = dipki::Cms::ReadEnvDataToBytes(cmsfile, "password12345");
	assert(bv.size() > 0);
	cout << "MSG='" << dipki::bvec2str(bv) << "'" << endl;

}

void test_CNV_ShortPathName()
{
	cout << "\nTEST SHORT PATH NAME FOR FILENAME IN CHINESE CHARACTERS:" << endl;
	// Filename in Chinese characters
	std::wstring wfname = L"你好.txt";
	std::string shortname = dipki::Cnv::ShortPathName(wfname);
	cout << "ShortPath='" << shortname << "'" << endl;
}

void test_FormatErrorMessage()
{
	cout << "\nTEST FORMAT ERROR MESSAGE:" << endl;
	int r = 0;
	cout << dipki::Err::FormatErrorMessage(11) << endl;
	// Try to create a signed-data object but passing a missing cert file
	std::string privkey = dipki::Rsa::ReadPrivateKey("AlicePrivRSASign.p8e", "password");
	try {
		r = dipki::Cms::MakeSigData("not-to-be-made.p7m", "excontent.txt", "missing.cert", privkey, dipki::Cms::SigAlg::Rsa_Sha1);
	}
	catch (std::exception& e) {
		cout << e.what() << endl;
	}
}

void test_RSA_ReadPublicKey_CSR()
{
	cout << "\nREAD PUBLIC KEY FROM CSR:" << endl;
	std::string csrfile, keyfile, dn, extns;
	std::string keystr;
	int r;

	// Create a new CSR for LAMPS WG alice
	csrfile = "lamps-alice-csr.pem";
	keyfile = "lamps-alice.p8"; // No password
	dn = "O=IETF;OU=LAMPS WG;CN=Alice Lovelace;";
	extns = "keyUsage=digitalSignature,nonRepudiation;extKeyUsage=emailProtection";

	r = dipki::X509::CertRequest(csrfile, keyfile, "", dn, extns, dipki::X509::SigAlg::Rsa_Sha256);

	cout << "X509::CertRequest returns " << r << " (expecting 0)" << endl;
	// Dump details of CSR we just made...
	cout << dipki::X509::TextDumpToString(csrfile, dipki::X509::OutputOpts::Ldap) << endl;

	// Read in public key from this CSR file  to an internal key string
	// (New in [v20.7])
	keystr = dipki::Rsa::ReadPublicKey(csrfile);
	cout << "Keysize=" << dipki::Rsa::KeyBits(keystr) << " bits, ";
	cout << "HashCode=0x" << std::hex << dipki::Rsa::KeyHashCode(keystr) << std::dec << endl;
	// EXPECTED: Keysize=2048 bits, HashCode=0xca0b84da
}

void test_X509_MakeCert_Ex()
{
	cout << "\nMAKE X.509 CERT WITH LATEST OPTIONS V20.7" << endl;
	int r;
	std::string dn, extns;
	std::string certname = "myca-newattributes2022.cer";
	std::string keyfile = "lamps-ca.rsa.p8"; // No password
	int serialNum = 0x88a;
	dn = "C=DE;dnQualifier='distinguisher';initials='E.M.';pseudonym='Super Muster';generationQualifier='3rd.';CN=Erika Mustermann";
	extns = "keyUsage=digitalSignature,nonRepudiation;extKeyUsage=emailProtection;rfc822Name=erika@example.com;cRLDistributionPoints=http://dodgycert.example.com/evca.crl;";

	r = dipki::X509::MakeCertSelf(certname, keyfile, "", serialNum, 10, dn, extns, dipki::X509::KeyUsageOptions::NoKeyUsageOption, dipki::X509::SigAlg::Rsa_Sha256);
	cout << "X509::MakeCertSelf returns " << r << " (expecting 0)" << endl;
	// Dump details of cert we just made...
	cout << dipki::X509::TextDumpToString(certname, dipki::X509::OutputOpts::Ldap) << endl;
	// Query the certificate
	cout << "cRLDistributionPointsURI='" << dipki::X509::QueryCert(certname, "cRLDistributionPointsURI") << "'" << endl;
}

void test_hash_SHA3() {
	cout << "\nTESTING SHA-3..." << endl;
	std::string hashstr;
	dipki::bvec_t hashbv;

	cout << "SHA-3-256('abc')=3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532" << endl;
	hashstr = dipki::Hash::HexFromString("abc", dipki::Hash::Alg::Sha3_256);
	cout << hashstr << endl;
	hashstr = dipki::Hash::HexFromBytes(dipki::Cnv::FromHex("616263"), dipki::Hash::Alg::Sha3_256);
	cout << hashstr << endl;
	hashstr = dipki::Hash::HexFromFile("abc.txt", dipki::Hash::Alg::Sha3_256);
	cout << hashstr << endl;
	hashbv = dipki::Hash::Bytes(dipki::Cnv::FromHex("616263"), dipki::Hash::Alg::Sha3_256);
	cout << dipki::Cnv::ToHex(hashbv) << endl;
	// Hash of the empty string SHA3-224 ... SHA3-512
	hashstr = dipki::Hash::HexFromString("", dipki::Hash::Alg::Sha3_224);
	cout << hashstr << endl;
	hashstr = dipki::Hash::HexFromString("", dipki::Hash::Alg::Sha3_256);
	cout << hashstr << endl;
	hashstr = dipki::Hash::HexFromString("", dipki::Hash::Alg::Sha3_384);
	cout << hashstr << endl;
	hashstr = dipki::Hash::HexFromString("", dipki::Hash::Alg::Sha3_512);
	cout << hashstr << endl;

}

void test_Xof() {
	cout << "\nTESTING XOF..." << endl;
	dipki::bvec_t bv;
	dipki::bvec_t msg = dipki::Cnv::FromHex("6ae23f058f0f2264a18cd609acc26dd4dbc00f5c3ee9e13ecaea2bb5a2f0bb6b");
	cout << "ALG=Shake256" << endl;
	cout << "MSG=" << dipki::Cnv::ToHex(msg) << endl;
	cout << "OUTLEN=2000 bits=250 bytes" << endl;
	// Output 2000 bits
	bv = dipki::Xof::Bytes(2000 / 8, msg, dipki::Xof::XofAlg::Shake256);
	cout << "OUT=" << dipki::Cnv::ToHex(bv) << endl;
	assert(dipki::Cnv::ToHex(bv) ==
		"B9B92544FB25CFE4EC6FE437D8DA2BBE00F7BDAFACE3DE97B8775A44D753C3ADCA3F7C6F183CC8647E229070439AA9539AE1F8F13470C9D3527FFFDEEF6C94F9F0520FF0C1BA8B16E16014E1AF43AC6D94CB7929188CCE9D7B02F81A2746F52BA16988E5F6D93298D778DFE05EA0EF256AE3728643CE3E29C794A0370E9CA6A8BF3E7A41E86770676AC106F7AE79E67027CE7B7B38EFE27D253A52B5CB54D6EB4367A87736ED48CB45EF27F42683DA140ED3295DFC575D3EA38CFC2A3697CC92864305407369B4ABAC054E497378DD9FD0C4B352EA3185CE1178B3DC1599DF69DB29259D4735320C8E7D33E8226620C9A1D22761F1D35BDFF79A");
	// MGF1-SHA-256
	msg = dipki::Cnv::FromHex("3b5c056af3ebba70d4c805380420585562b32410a778f558ff951252407647e3");
	cout << "ALG=Mgf1_Sha256" << endl;
	cout << "MSG=" << dipki::Cnv::ToHex(msg) << endl;
	cout << "OUTLEN=34 bytes" << endl;
	// Output 34 bytes
	bv = dipki::Xof::Bytes(34, msg, dipki::Xof::XofAlg::Mgf1_Sha256);
	cout << "OUT=" << dipki::Cnv::ToHex(bv) << endl;
	assert(dipki::Cnv::ToHex(bv) ==
		"5B7EB772AECF04C74AF07D9D9C1C1F8D3A90DCDA00D5BAB1DC28DAECDC86EB87611E");
	// One-liner
	cout << dipki::Cnv::ToHex(dipki::Xof::Bytes(34,
		dipki::Cnv::FromHex("3b5c056af3ebba70d4c805380420585562b32410a778f558ff951252407647e3"),
		dipki::Xof::XofAlg::Mgf1_Sha256)) << endl;

}

void test_Prf() {
	cout << "\nTESTING PRF..." << endl;
	dipki::bvec_t bv;
	dipki::bvec_t key = dipki::Cnv::FromHex("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F");
	dipki::bvec_t msg = dipki::Cnv::FromHex("00010203");
	std::string customString = "My Tagged Application";

	cout << "ALG=Kmac128" << endl;
	cout << "MSG=" << dipki::Cnv::ToHex(msg) << endl;
	cout << "KEY=" << dipki::Cnv::ToHex(key) << endl;
	cout << "CustomStr='" << customString << "'" << endl;
	cout << "OUTLEN=" << 256 / 8 << endl;
	bv = dipki::Prf::Bytes(256 / 8, msg, key, dipki::Prf::PrfAlg::Kmac128, customString);
	cout << "OUT=" << dipki::Cnv::ToHex(bv) << endl;
	assert(dipki::Cnv::ToHex(bv) == "3B1FBA963CD8B0B59E8C1A6D71888B7143651AF8BA0A7070C0979E2811324AA5");
}

void test_KMAC() {
	cout << "\nTESTING KMAC..." << endl;
	// Ref: `KMAC_samples.pdf` "Secure Hashing - KMAC-Samples" 2017-02-27
	/* Sample #1
	kmac_test(128, "404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F", "00010203",
	  256, "", "E5780B0D3EA6F7D3A429C5706AA43A00FADBD7D49628839E3187243F456EE14E");
	*/
	dipki::bvec_t bv;
	dipki::bvec_t key = dipki::Cnv::FromHex("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F");
	dipki::bvec_t msg = dipki::Cnv::FromHex("00010203");
	cout << "Sample #1" << endl;
	cout << "ALG=Kmac128" << endl;
	cout << "MSG=" << dipki::Cnv::ToHex(msg) << endl;
	cout << "KEY=" << dipki::Cnv::ToHex(key) << endl;
	cout << "OUTLEN=" << 256 / 8 << endl;
	bv = dipki::Prf::Bytes(256 / 8, msg, key, dipki::Prf::PrfAlg::Kmac128);
	cout << "OUT=" << dipki::Cnv::ToHex(bv) << endl;
	assert(dipki::Cnv::ToHex(bv) == "E5780B0D3EA6F7D3A429C5706AA43A00FADBD7D49628839E3187243F456EE14E");

	// One-liner
	cout << dipki::Cnv::ToHex(dipki::Prf::Bytes(256 / 8, dipki::Cnv::FromHex("00010203"), 
		dipki::Cnv::FromHex("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F"), 
		dipki::Prf::PrfAlg::Kmac128)) << endl;
	// E5780B0D3EA6F7D3A429C5706AA43A00FADBD7D49628839E3187243F456EE14E

	/* Sample #5
	kmac_test(256, "404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F", 
	  "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7",
	  256, "", "75358CF39E41494E949707927CEE0AF20A3FF553904C86B08F21CC414BCFD691589D27CF5E15369CBBFF8B9A4C2EB17800855D0235FF635DA82533EC6B759B69");
	*/
	msg = dipki::Cnv::FromHex("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7");
	cout << "Sample #5" << endl;
	cout << "ALG=Kmac256" << endl;
	cout << "MSG=" << dipki::Cnv::ToHex(msg) << endl;
	cout << "KEY=" << dipki::Cnv::ToHex(key) << endl;
	cout << "OUTLEN=" << 512 / 8 << endl;
	bv = dipki::Prf::Bytes(512 / 8, msg, key, dipki::Prf::PrfAlg::Kmac256);
	cout << "OUT=" << dipki::Cnv::ToHex(bv) << endl;
	assert(dipki::Cnv::ToHex(bv) == "75358CF39E41494E949707927CEE0AF20A3FF553904C86B08F21CC414BCFD691589D27CF5E15369CBBFF8B9A4C2EB17800855D0235FF635DA82533EC6B759B69");
}

void do_all() {
	test_Cnv();
	test_Cnv_base58();
	test_Cnv_byteutils();
	test_Cnv_utf8();
	test_Cipher();
	test_Cipher_Aead();
	test_Cipher_KeyWrap();
	test_Cipher_Pad();
	test_Cipher_File();
	test_Rng();
	test_Gen_Errors();
	test_Asn1();
	test_Hash();
	test_Hmac();
	test_Wipe();
	test_Pbe();
	test_Pem();
	test_Smime();
	test_Pwd(doPrompt);
	test_Pfx();
	test_Ocsp();
	test_Compr();
	test_Rsa();
	test_Rsa_SaveKey();
	test_Rsa_Xml();
	test_Rsa_Encode();
	test_Rsa_Encrypt();
	test_Rsa_Sign();
	test_Ecc();
	test_Ecc_ByCurve();
	test_Ecc_SaveKey();
	test_Ecc_ECDH();
	test_Sig();
	test_Sig_Ecc();
	test_Sig_Ecc_det();
	test_Sig_PSS();
	test_X509_MakeCert();
	test_X509_Certs();
	test_X509_CertRequest();
	test_X509_CRL();
	test_CMS_SigData();
	test_CMS_EnvData();
	test_CMS();
	test_CMS_SigData_PSS();
	test_CMS_SMIME_Chain();
	test_CMS_Data_Bytes();
	test_CMS_SigValue();
	test_CMS_Pseudo();
	test_ReadJWK();
	test_HASH_Length();
	test_KDF_Bytes();
	test_KDF_ForCms();
	test_CMS_EnvData_ecdh();
	test_CMS_EnvData_adv();
	test_CNV_ShortPathName();
	test_FormatErrorMessage();
	test_Cipher_GCM();
	test_RSA_ReadPublicKey_CSR();
	test_X509_MakeCert_Ex();
	test_hash_SHA3();
	test_hmac_SHA3();
	test_Xof();
	test_Prf();
	test_KMAC();
}

int main(int argc, char *argv[])
{
	// Set up console and locale for UTF-8 characters
	std::setlocale(LC_ALL, "en_US.utf8");
#ifdef _WIN32
	// Set console display page to UTF-8
	SetConsoleOutputCP(CP_UTF8);
#endif
	// Display basic info...
	cout << "__cplusplus=" << __cplusplus << endl;
	cout << "Gen::Version=" << dipki::Gen::Version() << endl;
	cout << "Gen::CompileTime=" << dipki::Gen::CompileTime() << endl;
	cout << "Gen::ModuleName=" << dipki::Gen::ModuleName() << endl;
	cout << "Gen::Platform=" << dipki::Gen::Platform() << endl;
	cout << "Gen::LicenceType=" << dipki::Gen::LicenceType() << endl;
	cout << "Gen::ModuleInfo=" << dipki::Gen::ModuleInfo() << endl;

	/* Handle command-line arguments
	* [some]      just do some tests (default = do all)
	* [prompt]    prompt for keystrokes and passwords
	*/
	// Local
	bool doSome = false;
	// Global
	doPrompt = false;

	// Put C-style arguments into a proper STL array
	std::vector<std::string> arguments(argv, argv + argc);
	//for (std::string& s : arguments) {
	//	// do stuff...
	//}

	for (unsigned int iarg = 1; iarg < arguments.size(); iarg++) {
		if (arguments[iarg] == "some")
			doSome = true;
		if (arguments[iarg] == "prompt")
			doPrompt = true;
	}

	//*************
	// DO THE TESTS
	//*************
	try {	// Overall try to catch any errors at all.
		if (doSome) // Use "some" in the command line
		{   // Do some tests - comment these out as required
			cout << "DOING SOME TESTS..." << endl;

			//test_Cnv();
			//test_Cnv_base58();
			//test_Cnv_byteutils();
			//test_Cnv_utf8();
			//test_Cipher();
			//test_Cipher_Aead();
			//test_Cipher_KeyWrap();
			//test_Cipher_Pad();
			//test_Cipher_File();
			//test_Rng();
			//test_Gen_Errors();
			//test_Asn1();
			//test_Hash();
			//test_Hmac();
			//test_Wipe();
			//test_Pbe();
			//test_Pem();
			//test_Smime();
			//test_Pwd(doPrompt);
			//test_Pfx();
			//test_Ocsp();
			//test_Compr();
			//test_Rsa();
			//test_Rsa_SaveKey();
			//test_Rsa_Xml();
			//test_Rsa_Encode();
			//test_Rsa_Encrypt();
			//test_Rsa_Sign();
			//test_Ecc();
			//test_Ecc_ByCurve();
			//test_Ecc_SaveKey();
			//test_Ecc_ECDH();
			//test_Sig();
			//test_Sig_Ecc();
			//test_Sig_Ecc_det();
			//test_Sig_PSS();
			//test_X509_MakeCert();
			//test_X509_Certs();
			//test_X509_CertRequest();
			//test_X509_CRL();
			//test_CMS_SigData();
			//test_CMS_EnvData();
			//test_CMS();
			//test_CMS_SigData_PSS();
			//test_CMS_SMIME_Chain();
			//test_CMS_Data_Bytes();
			//test_CMS_SigValue();
			//test_CMS_Pseudo();
			//test_ReadJWK();
			//test_HASH_Length();
			//test_KDF_Bytes();
			//test_KDF_ForCms();
			//test_CMS_EnvData_ecdh();
			// [v20.6]
			//test_CMS_EnvData_adv();
			// [v21.0]
			//test_CNV_ShortPathName();
			//test_FormatErrorMessage();
			//test_Cipher_GCM();
			//test_RSA_ReadPublicKey_CSR();
			//test_X509_MakeCert_Ex();
			//test_hash_SHA3();
			//test_hmac_SHA3();
			test_Xof();
			//test_Prf();
			test_KMAC();

		}
		else {
			cout << "DOING ALL TESTS..." << endl;
			do_all();
		}
		cout << "\nALL DONE." << endl;
	}
	catch (std::exception& e) {
		cout << e.what() << endl;
		cout << "TESTS FAILED TO COMPLETE!!" << endl;
	}

	return 0;
}