/*  $Id: TestAPI.cpp $
*   Last updated:
*   $Date: 2021-09-25 08:37:00 $
*   $Version: 6.20.0 $
*/

/******************************* LICENSE ***********************************
* Copyright (C) 2021 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 <iostream>
#include <assert.h>
#include <fstream>
#include <sstream>
#include "crsysapi.hpp"

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

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

/** Read in a file to a string in one go. */
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. */
crsysapi::bvec_t read_binary_file(const std::string& path) {
	std::ifstream stream(path, std::ios::in | std::ios::binary);
	crsysapi::bvec_t bv((std::istreambuf_iterator<char>(stream)), std::istreambuf_iterator<char>());
	return bv;
}

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

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

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

	cout << "Expecting errors..." << endl;
	try {
		bv = crsysapi::Cnv::FromHex("BADHEXXX");
		cout << "bv.size=" << bv.size() << endl;
	}
	catch (std::exception& e) {
		cout << e.what() << endl;
	}
	try {
		bv = crsysapi::Cnv::FromBase64("BADBASE64%#@");
		cout << "bv.size=" << bv.size() << endl;
	}
	catch (std::exception& e) {
		cout << e.what() << endl;
	}

}

void test_Hash() {
	cout << "\nTESTING Hash..." << endl;
	std::string hashstr;
	crsysapi::bvec_t hashbv;
	crsysapi::Hash::Alg hashAlg;

	hashstr = crsysapi::Hash::HexFromHex("616263");
	cout << "'" << hashstr << "'" << endl;
	hashstr = crsysapi::Hash::HexFromHex("616263", crsysapi::Hash::Alg::Sha224);
	cout << "'" << hashstr << "'" << endl;
	hashbv = crsysapi::Hash::BytesFromBytes(crsysapi::Cnv::FromHex("616263"));
	cout << crsysapi::Cnv::ToHex(hashbv) << endl;
	hashbv = crsysapi::Hash::BytesFromBytes(crsysapi::Cnv::FromHex("616263"), crsysapi::Hash::Alg::Sha224);
	cout << crsysapi::Cnv::ToHex(hashbv) << endl;
	hashbv = crsysapi::Hash::BytesFromBytes(crsysapi::Cnv::FromHex("616263"), crsysapi::Hash::Alg::Sha256);
	cout << crsysapi::Cnv::ToHex(hashbv) << endl;
	crsysapi::Hash::Alg myhashalg;
	myhashalg = crsysapi::Hash::Alg::Sha256;
	hashbv = crsysapi::Hash::BytesFromBytes(crsysapi::Cnv::FromHex("616263"), myhashalg);
	cout << crsysapi::Cnv::ToHex(hashbv) << endl;

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

	myhashalg = crsysapi::Hash::Alg::Sha3_256;
	cout << "SHA-3-256:" << endl;
	hashstr = crsysapi::Hash::HexFromString("abc", myhashalg);
	cout << "'" << hashstr << "'" << endl;
	hashstr = crsysapi::Hash::HexFromBytes(crsysapi::Cnv::FromHex("616263"), myhashalg);
	cout << "'" << hashstr << "'" << endl;

	cout << "EXPECTING ERROR: FILE IS MISSING..." << endl;
	try {
		hashbv = crsysapi::Hash::BytesFromFile("missing.file", myhashalg);
		cout << crsysapi::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 = crsysapi::Hash::HexFromString("");
	cout << "'" << hashstr << "'" << endl;
	hashbv = crsysapi::Hash::BytesFromBytes(crsysapi::bvec_t());
	cout << crsysapi::Cnv::ToHex(hashbv) << endl;
	hashstr = crsysapi::Hash::HexFromHex("");
	cout << "'" << hashstr << "'" << endl;
	hashbv = crsysapi::Hash::BytesFromFile("empty.txt");
	cout << crsysapi::Cnv::ToHex(hashbv) << endl;
	hashstr = crsysapi::Hash::HexFromFile("empty.txt");
	cout << "'" << hashstr << "'" << endl;
	cout << "SHA-3-384 hash the empty string..." << endl;
	hashstr = crsysapi::Hash::HexFromString("", crsysapi::Hash::Alg::Sha3_384);
	cout << "'" << hashstr << "'" << endl;

	// Hash a text file: test files have different line endings (Windows CR-LF vs Unix LF)
	// but should give same result when using HexFromTextFile
	cout << "Hash a text file..." << endl;
	cout << "Contents = \"hello\\n\" in Windows (CRLF) and Unix (LF) forms" << endl;
	hashstr = crsysapi::Hash::HexFromFile("hello-crlf.txt");
	cout << "Binary CRLF: '" << hashstr << "'" << endl;
	hashstr = crsysapi::Hash::HexFromFile("hello-unix.txt");
	cout << "Binary UNIX: '" << hashstr << "'" << endl;
	hashstr = crsysapi::Hash::HexFromTextFile("hello-crlf.txt");
	cout << "Text CRLF:   '" << hashstr << "'" << endl;
	assert(hashstr == "22596363b3de40b06f981fb85d82312e8c0ed511");
	hashstr = crsysapi::Hash::HexFromTextFile("hello-unix.txt");
	cout << "Text UNIX:   '" << hashstr << "'" << endl;

	cout << "Hash bit strings of length 9 and 22 bits using SHA-1 and SHA-3-256 respectively..." << endl;
	cout << crsysapi::Hash::HexFromBits(crsysapi::Cnv::FromHex("5180"), 9) << endl;
	assert(0 == crsysapi::Hash::HexFromBits(crsysapi::Cnv::FromHex("5180"), 9).compare("0f582fa68b71ecdf1dcfc4946019cf5a18225bd2"));
	// SHAVS-SHA3 CAVS 19.0 "SHA3-256 ShortMsg" 
	hashstr = crsysapi::Hash::HexFromBits(crsysapi::Cnv::FromHex("2590A0"), 22, crsysapi::Hash::Alg::Sha3_256);
	cout << hashstr << endl;
	assert(hashstr == "d5863d4b1ff41551c92a9e08c52177e32376c9bd100c611c607db840096eb22f");

	cout << "EXPECTING ERRORS..." << endl;
	try {
		crsysapi::Hash::HexFromBits(crsysapi::Cnv::FromHex("5180"), 666);	// Too many bits for byte array
	}
	catch (std::exception& e) {
		cout << e.what() << endl;
	}
	try {
		crsysapi::Hash::HexFromBits(crsysapi::Cnv::FromHex("5180"), 9, crsysapi::Hash::Alg::Rmd160);	// Unsupported algorithm
	}
	catch (std::exception& e) {
		cout << e.what() << endl;
	}
	cout << "...END OF EXPECTED ERRORS." << endl;

	// Demonstrate lookups for hash algorithm names and digest length
	cout << "Length(" << crsysapi::Hash::AlgName(crsysapi::Hash::Alg::Sha1) << ")=" << crsysapi::Hash::LengthInBytes(crsysapi::Hash::Alg::Sha1) << "  ";
	cout << "Length(" << crsysapi::Hash::AlgName(crsysapi::Hash::Alg::Sha256) << ")=" << crsysapi::Hash::LengthInBytes(crsysapi::Hash::Alg::Sha256) << "  ";
	hashAlg = crsysapi::Hash::Alg::Md5;
	cout << "Length(" << crsysapi::Hash::AlgName(hashAlg) << ")=" << crsysapi::Hash::LengthInBytes(hashAlg) << "  ";
	hashAlg = crsysapi::Hash::Alg::Sha3_384;
	cout << "Length(" << crsysapi::Hash::AlgName(hashAlg) << ")=" << crsysapi::Hash::LengthInBytes(hashAlg) << "  ";
	hashAlg = crsysapi::Hash::Alg::Rmd160;
	cout << "Length(" << crsysapi::Hash::AlgName(hashAlg) << ")=" << crsysapi::Hash::LengthInBytes(hashAlg) << "  ";
	cout << endl;

}

void test_Hash_Obj() {
	cout << "\nTESTING Hash object..." << endl;
	crsysapi::bvec_t hashbv;
	bool isok;

	crsysapi::Hash oHash;
	isok = oHash.Init(crsysapi::Hash::Alg::Sha256);
	assert(isok);

	oHash.AddData("a");
	oHash.AddData("bc");
	hashbv = oHash.Final();
	cout << "SHA256('abc')=" << crsysapi::Cnv::ToHex(hashbv) << endl;
	assert(crsysapi::Cnv::ToHex(hashbv) == "BA7816BF8F01CFEA414140DE5DAE2223B00361A396177A9CB410FF61F20015AD");

	// One million repetitions of the character "a" (0x61)
	// Ref: https://www.di-mgt.com.au/sha_testvectors.html
	isok = oHash.Init(crsysapi::Hash::Alg::Sha3_512);
	assert(isok);
	crsysapi::bvec_t a1000(1000, 0x61);
	for (int i = 0; i < 1000; i++) {
		oHash.AddData(a1000);
	}
	hashbv = oHash.Final();
	cout << "SHA-3-512(1m x 'a')=" << crsysapi::Cnv::ToHex(hashbv) << endl;
	assert(crsysapi::Cnv::ToHex(hashbv) == "3C3A876DA14034AB60627C077BB98F7E120A2A5370212DFFB3385A18D4F38859ED311D0A9D5141CE9CC5C66EE689B266A8AA18ACE8282A0E0DB596C90B0A7B87");


	cout << "\nEXPECTED ERROR: unsupported algorithm" << endl;
	try {
		isok = oHash.Init(crsysapi::Hash::Alg::Rmd160);
		assert(isok);
	}
	catch (std::exception& e) {
		cout << e.what() << endl;
	}
	cout << "-----END EXPECTED ERROR" << endl;
}

void test_Mac() {
	cout << "\nTESTING Mac..." << endl;
	std::string macstr, keyHex, msgHex;
	crsysapi::bvec_t macbv, bv, data, key;
	//crsysapi::Mac::Alg macAlg;

	cout << "Test case 1 from RFC 2202 and RFC 4231" << endl;
	// HEX('Hi There') = 4869205468657265
	macstr = crsysapi::Mac::HexFromHex("4869205468657265", "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b");
	cout << "HMAC-SHA-1='" << macstr << "'" << endl;
	assert(macstr == "b617318655057264e28bc0b6fb378c8ef146be00");
	macstr = crsysapi::Mac::HexFromHex("4869205468657265", "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", crsysapi::Mac::Alg::HmacSha256);
	cout << "HMAC-SHA-256='" << macstr << "'" << endl;
	assert(macstr == "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7");

	cout << "Test case 4 from RFC 2202 and RFC 4231" << endl;
	key = crsysapi::Cnv::FromHex("0102030405060708090a0b0c0d0e0f10111213141516171819");
	cout << "key=" << crsysapi::Cnv::ToHex(key) << endl;
	// data = 0xcd repeated 50 times
	data.clear();
	data.insert(data.begin(), 50, (unsigned char)0xcd);
	cout << "data=" << crsysapi::Cnv::ToHex(data) << endl;
	bv = crsysapi::Mac::BytesFromBytes(data, key);
	cout << "HMAC-SHA-1=" << crsysapi::Cnv::ToHex(bv) << endl;
	assert(bv == crsysapi::Cnv::FromHex("4c9007f4026250c6bc8414f9bf50c86c2d7235da"));
	bv = crsysapi::Mac::BytesFromBytes(data, key, crsysapi::Mac::Alg::HmacMd5);
	cout << "HMAC-MD5=" << crsysapi::Cnv::ToHex(bv) << endl;
	assert(bv == crsysapi::Cnv::FromHex("697eaf0aca3a3aea3a75164746ffaa79"));
	bv = crsysapi::Mac::BytesFromBytes(data, key, crsysapi::Mac::Alg::HmacSha256);
	cout << "HMAC-SHA-256=" << crsysapi::Cnv::ToHex(bv) << endl;
	assert(bv == crsysapi::Cnv::FromHex("82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b"));
	macstr = crsysapi::Mac::HexFromBytes(data, key, crsysapi::Mac::Alg::HmacSha384);
	cout << "HMAC-SHA-384='" << macstr << "'" << endl;
	assert(macstr == "3e8a69b7783c25851933ab6290af6ca77a9981480850009cc5577c6e1f573b4e6801dd23c4a7d679ccf8a386c674cffb");
	macstr = crsysapi::Mac::HexFromBytes(data, key, crsysapi::Mac::Alg::HmacSha224);
	cout << "HMAC-SHA-224='" << macstr << "'" << endl;
	assert(macstr == "6c11506874013cac6a2abc1bb382627cec6a90d86efc012de7afec5a");
	macstr = crsysapi::Mac::HexFromBytes(data, key, crsysapi::Mac::Alg::HmacSha512);
	cout << "HMAC-SHA-512='" << macstr << "'" << endl;
	assert(macstr == "b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3dba91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd");
	// Compute HMAC-RIPEMD-160 then check against known test vector (RFC2286)
	macstr = crsysapi::Mac::HexFromBytes(data, key, crsysapi::Mac::Alg::HmacRipemd160);
	cout << "HMAC-RIPEMD-160='" << macstr << "'" << endl;
	assert(macstr == "d5ca862f4d21d5e610e18b4cf1beb97a4365ecf4");


	cout << "Test case 7 from RFC 4231" << endl;
	data = crsysapi::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 = crsysapi::Mac::BytesFromBytes(data, key, crsysapi::Mac::Alg::HmacSha224);
	cout << "HMAC-SHA-224=" << crsysapi::Cnv::ToHex(bv) << endl;
	assert(bv == crsysapi::Cnv::FromHex("3a854166ac5d9f023f54d517d0b39dbd946770db9c2b95c9f6f565d1"));

	// HMAC-SHA3-256
	cout << "HMAC-SHA3..." << endl;
	// Text is "Sample message for keylen<blocklen"
	msgHex = "53616D706C65206D65737361676520666F72206B65796C656E3C626C6F636B6C656E";
	keyHex = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f";
	macstr = crsysapi::Mac::HexFromHex(msgHex, keyHex, crsysapi::Mac::Alg::HmacSha3_256);
	cout << "HMAC-SHA3-256='" << macstr << "'" << endl;
	assert(macstr == "4fe8e202c4f058e8dddc23d8c34e467343e23555e24fc2f025d598f558f67205");

	cout << "CMAC tests from SP800-38B..." << endl;
	keyHex = "2b7e151628aed2a6abf7158809cf4f3c";
	// CMAC-AES-128 on the empty string
	msgHex = "";
	macstr = crsysapi::Mac::HexFromHex(msgHex, keyHex, crsysapi::Mac::Alg::CmacAes128);
	cout << "CMAC-AES-128(K128, '')='" << macstr << "'" << endl;
	assert(macstr == "bb1d6929e95937287fa37d129b756746");

	/* CMAC_AES-256 on Example 12: Mlen = 512 */
	keyHex = "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4";
	msgHex = "6bc1bee22e409f96e93d7e117393172a"
		"ae2d8a571e03ac9c9eb76fac45af8e51"
		"30c81c46a35ce411e5fbc1191a0a52ef"
		"f69f2445df4f9b17ad2b417be66c3710";
	macstr = crsysapi::Mac::HexFromHex(msgHex, keyHex, crsysapi::Mac::Alg::CmacAes256);
	cout << "CMAC-AES-256(K256, M512)='" << macstr << "'" << endl;
	assert(macstr == "e1992190549f6ed5696a2c056c315410");

	// POLY1305 AUTHENTICATION ALGORITHM
	// Ref: Test vector from `draft-irtf-cfrg-chacha20-poly1305-06.txt`
	keyHex = "85d6be7857556d337f4452fe42d506a80103808afb0db2fd4abff6af4149f51b";
	msgHex = crsysapi::Cnv::ToHex("Cryptographic Forum Research Group");
	macstr = crsysapi::Mac::HexFromHex(msgHex, keyHex, crsysapi::Mac::Alg::Poly1305);
	cout << "POLY1305='" << macstr << "'" << endl;
	assert(macstr == "a8061dc1305136c6c22b8baf0c0127a9");

	// KMAC
	// Ref: `KMAC_samples.pdf` "Secure Hashing - KMAC-Samples" 2017-02-27
	// Sample #1
	keyHex = "404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F";
	msgHex = "00010203";
	macstr = crsysapi::Mac::HexFromHex(msgHex, keyHex, crsysapi::Mac::Alg::Kmac128);
	cout << "KMAC128='" << macstr << "'" << endl;
	assert(macstr == "e5780b0d3ea6f7d3a429c5706aa43a00fadbd7d49628839e3187243f456ee14e");
}


void test_Mac_Obj() {
	cout << "\nTESTING Mac object..." << endl;
	crsysapi::bvec_t bv, key, data10;
	bool isok;

	key = crsysapi::Cnv::FromHex("0102030405060708090a0b0c0d0e0f10111213141516171819");
	cout << "key=" << crsysapi::Cnv::ToHex(key) << endl;
	// data = 0xcd repeated 50 times
	data10.clear();
	// make block of 10
	data10.insert(data10.begin(), 10, (unsigned char)0xcd);
	cout << "data10=" << crsysapi::Cnv::ToHex(data10) << endl;

	crsysapi::Mac oMac;
	isok = oMac.Init(key, crsysapi::Mac::Alg::HmacSha256);
	assert(isok);

	for (int i = 0; i < 5; i++) {
		oMac.AddData(data10);
	}
	bv = oMac.Final();
	cout << crsysapi::Cnv::ToHex(bv) << endl;
	assert(crsysapi::Cnv::ToHex(bv) == "82558A389A443C0EA4CC819899F2083A85F0FAA3E578F8077A2E3FF46729665B");

	cout << "EXPECTED ERROR: unsupported algorithm" << endl;
	try {
		isok = oMac.Init(key, crsysapi::Mac::Alg::CmacAes256);
		assert(isok);
	}
	catch (std::exception& e) {
		cout << e.what() << endl;
	}
}

void test_Rng() {
	crsysapi::bvec_t bv;
	cout << "\nTESTING Rng..." << endl;
	cout << "Rng::Hex:" << endl;
	for (int i = 0; i < 4; i++) {
		std::string s = crsysapi::Rng::Hex(32);
		cout << s << endl;
	}
	cout << "Rng::Number(-5,+5):" << endl;
	for (int i = 0; i < 20; i++) {
		cout << crsysapi::Rng::Number(-5, 5) << " ";
	}
	cout << endl;
	cout << "Rng::Octet:" << endl;
	for (int i = 0; i < 20; i++) {
		cout << crsysapi::Rng::Octet() << " ";
	}
	cout << endl;

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

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

}

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

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

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

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

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

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

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

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

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

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

	try {
		dt = crsysapi::Cipher::Decrypt(ct, key, iv, crsysapi::Cipher::Alg::Aes128, crsysapi::Cipher::Mode::CBC);
		cout << "dt=" << crsysapi::Cnv::ToHex(dt) << endl;
	}
	catch (std::exception& e) {
		cout << e.what() << endl;
	}
	cout << "Passing an invalid IV..." << endl;
	try {
		dt = crsysapi::Cipher::EncryptBlock(ct, key, crsysapi::bvec_t(), crsysapi::Cipher::Alg::Aes128, crsysapi::Cipher::Mode::CBC);
		cout << "dt=" << crsysapi::Cnv::ToHex(dt) << endl;
	}
	catch (std::exception& e) {
		cout << e.what() << endl;
	}
	cout << "Triple DES Known Answer tests..." << endl;
	key = crsysapi::Cnv::FromHex("385D7189A5C3D485E1370AA5D408082B5CCCCB5E19F2D90E");
	iv = crsysapi::Cnv::FromHex("C141B5FCCD28DC8A");
	pt = crsysapi::Cnv::FromHex("6E1BD7C6120947A464A6AAB293A0F89A563D8D40D3461B68");
	crsysapi::Cipher::Alg alg = crsysapi::Cipher::Alg::Tdea;
	crsysapi::Cipher::Mode mode;
	//char sCorrectECB[] = "64EAAD4ACBB9CEAD6C7615E7C7E4792FE587D91F20C7D2F4";
	//char sCorrectCBC[] = "6235A461AFD312973E3B4F7AA7D23E34E03371F8E8C376C9";
	//char sCorrectCFB[] = "E26BA806A59B0330DE40CA38E77A3E494BE2B212F6DD624B";
	//char sCorrectOFB[] = "E26BA806A59B03307DE2BCC25A08BA40A8BA335F5D604C62";
	//char sCorrectCTR[] = "E26BA806A59B03303C62C2EFF32D3ACDD5D5F35EBCC53371";
	mode = crsysapi::Cipher::Mode::ECB;
	cout << crsysapi::Cipher::AlgName(alg) << "/" << crsysapi::Cipher::ModeName(mode) << endl;
	cout << "pt=" << crsysapi::Cnv::ToHex(pt) << endl;
	ct = crsysapi::Cipher::EncryptBlock(pt, key, iv, alg, mode);
	cout << "ct=" << crsysapi::Cnv::ToHex(ct) << endl;
	assert(0 == crsysapi::Cnv::ToHex(ct).compare("64EAAD4ACBB9CEAD6C7615E7C7E4792FE587D91F20C7D2F4"));
	dt = crsysapi::Cipher::DecryptBlock(ct, key, iv, alg, mode);
	cout << "dt=" << crsysapi::Cnv::ToHex(dt) << endl;
	assert(0 == crsysapi::Cnv::ToHex(dt).compare("6E1BD7C6120947A464A6AAB293A0F89A563D8D40D3461B68"));
	mode = crsysapi::Cipher::Mode::CBC;
	cout << crsysapi::Cipher::AlgName(alg) << "/" << crsysapi::Cipher::ModeName(mode) << endl;
	cout << "pt=" << crsysapi::Cnv::ToHex(pt) << endl;
	ct = crsysapi::Cipher::EncryptBlock(pt, key, iv, alg, mode);
	cout << "ct=" << crsysapi::Cnv::ToHex(ct) << endl;
	assert(0 == crsysapi::Cnv::ToHex(ct).compare("6235A461AFD312973E3B4F7AA7D23E34E03371F8E8C376C9"));
	dt = crsysapi::Cipher::DecryptBlock(ct, key, iv, alg, mode);
	cout << "dt=" << crsysapi::Cnv::ToHex(dt) << endl;
	assert(0 == crsysapi::Cnv::ToHex(dt).compare("6E1BD7C6120947A464A6AAB293A0F89A563D8D40D3461B68"));
	mode = crsysapi::Cipher::Mode::CFB;
	cout << crsysapi::Cipher::AlgName(alg) << "/" << crsysapi::Cipher::ModeName(mode) << endl;
	cout << "pt=" << crsysapi::Cnv::ToHex(pt) << endl;
	ct = crsysapi::Cipher::EncryptBlock(pt, key, iv, alg, mode);
	cout << "ct=" << crsysapi::Cnv::ToHex(ct) << endl;
	assert(0 == crsysapi::Cnv::ToHex(ct).compare("E26BA806A59B0330DE40CA38E77A3E494BE2B212F6DD624B"));
	dt = crsysapi::Cipher::DecryptBlock(ct, key, iv, alg, mode);
	cout << "dt=" << crsysapi::Cnv::ToHex(dt) << endl;
	assert(0 == crsysapi::Cnv::ToHex(dt).compare("6E1BD7C6120947A464A6AAB293A0F89A563D8D40D3461B68"));
	mode = crsysapi::Cipher::Mode::OFB;
	cout << crsysapi::Cipher::AlgName(alg) << "/" << crsysapi::Cipher::ModeName(mode) << endl;
	cout << "pt=" << crsysapi::Cnv::ToHex(pt) << endl;
	ct = crsysapi::Cipher::EncryptBlock(pt, key, iv, alg, mode);
	cout << "ct=" << crsysapi::Cnv::ToHex(ct) << endl;
	assert(0 == crsysapi::Cnv::ToHex(ct).compare("E26BA806A59B03307DE2BCC25A08BA40A8BA335F5D604C62"));
	dt = crsysapi::Cipher::DecryptBlock(ct, key, iv, alg, mode);
	cout << "dt=" << crsysapi::Cnv::ToHex(dt) << endl;
	assert(0 == crsysapi::Cnv::ToHex(dt).compare("6E1BD7C6120947A464A6AAB293A0F89A563D8D40D3461B68"));
	mode = crsysapi::Cipher::Mode::CTR;
	cout << crsysapi::Cipher::AlgName(alg) << "/" << crsysapi::Cipher::ModeName(mode) << endl;
	cout << "pt=" << crsysapi::Cnv::ToHex(pt) << endl;
	ct = crsysapi::Cipher::EncryptBlock(pt, key, iv, alg, mode);
	cout << "ct=" << crsysapi::Cnv::ToHex(ct) << endl;
	assert(0 == crsysapi::Cnv::ToHex(ct).compare("E26BA806A59B03303C62C2EFF32D3ACDD5D5F35EBCC53371"));
	dt = crsysapi::Cipher::DecryptBlock(ct, key, iv, alg, mode);
	cout << "dt=" << crsysapi::Cnv::ToHex(dt) << endl;
	assert(0 == crsysapi::Cnv::ToHex(dt).compare("6E1BD7C6120947A464A6AAB293A0F89A563D8D40D3461B68"));

}

void test_Cipher_Obj() {
	cout << "\nTESTING Cipher object..." << endl;
	crsysapi::bvec_t pt, ct;
	crsysapi::bvec_t key, iv;
	crsysapi::Cipher::Alg alg = crsysapi::Cipher::Alg::Aes128;
	crsysapi::Cipher::Mode mode;
	bool isok;
	key = crsysapi::Cnv::FromHex("0123456789ABCDEFF0E1D2C3B4A59687");
	iv = crsysapi::Cnv::FromHex("FEDCBA9876543210FEDCBA9876543210");

	crsysapi::Cipher oCipher;
	// AES-128 CBC mode
	mode = crsysapi::Cipher::Mode::CBC;
	isok = oCipher.InitEncrypt(key, iv, alg, mode);
	assert(isok);
	cout << crsysapi::Cipher::AlgName(alg) << "/" << crsysapi::Cipher::ModeName(mode) << endl;

	// Pass data in chunks of 16 bytes (or multiples of)
	// Part 1...
	pt = crsysapi::str2bvec("Now is the time for all good men");
	cout << "PT=" << crsysapi::Cnv::ToHex(pt) << endl;
	ct = oCipher.Update(pt);
	cout << "CT=" << crsysapi::Cnv::ToHex(ct) << endl;
	// Part 2...
	pt = crsysapi::str2bvec(" to come to the aid of the party");
	cout << "PT=" << crsysapi::Cnv::ToHex(pt) << endl;
	ct = oCipher.Update(pt);
	cout << "CT=" << crsysapi::Cnv::ToHex(ct) << endl;
	// We are done
	isok = oCipher.Final();
	assert(isok);

	// AES-128 counter (CTR) mode
	mode = crsysapi::Cipher::Mode::CTR;
	isok = oCipher.InitEncrypt(key, iv, alg, mode);
	assert(isok);
	cout << crsysapi::Cipher::AlgName(alg) << "/" << crsysapi::Cipher::ModeName(mode) << endl;

	// Pass data in chunks of 16 bytes (or multiples of)
	// Part 1...
	pt = crsysapi::str2bvec("Now is the time for all good men");
	cout << "PT=" << crsysapi::Cnv::ToHex(pt) << endl;
	ct = oCipher.Update(pt);
	cout << "CT=" << crsysapi::Cnv::ToHex(ct) << endl;
	// Part 2...
	// In CTR mode (and OFB and CFB) the last block need not be a multiple
	pt = crsysapi::str2bvec(" to come to the aid of");
	cout << "PT=" << crsysapi::Cnv::ToHex(pt) << endl;
	ct = oCipher.Update(pt);
	cout << "CT=" << crsysapi::Cnv::ToHex(ct) << endl;
	// We are done
	isok = oCipher.Final();
	assert(isok);

	// Now decrypt...
	cout << "Decrypting..." << endl;
	mode = crsysapi::Cipher::Mode::ECB;
	isok = oCipher.InitDecrypt(key, iv, alg, mode);
	assert(isok);
	cout << crsysapi::Cipher::AlgName(alg) << "/" << crsysapi::Cipher::ModeName(mode) << endl;
	ct = crsysapi::Cnv::FromHex("F0D1AD6F901FFFAE5572A6928DAB52B064B25C79F876730321E36DC01011ACCE");
	cout << "CT=" << crsysapi::Cnv::ToHex(ct) << endl;
	pt = oCipher.Update(ct);
	cout << "PT=" << crsysapi::bvec2str(pt) << endl;
	ct = crsysapi::Cnv::FromHex("02D6F240643271217758C44862BB1B6862CD10757387256BC678A1708483862A");
	cout << "CT=" << crsysapi::Cnv::ToHex(ct) << endl;
	pt = oCipher.Update(ct);
	cout << "PT=" << crsysapi::bvec2str(pt) << endl;

}

void test_CipherStream() {
	crsysapi::bvec_t bv;
	crsysapi::bvec_t pt, ct, dt, key, iv, data;
	std::string fileIn, fileOut;
	int r;

	cout << "\nTESTING CipherStream..." << endl;

	cout << "Arcfour (RC4) test vector" << endl;
	key = crsysapi::Cnv::FromHex("0123456789abcdef");
	pt = crsysapi::Cnv::FromHex("0123456789abcdef");
	cout << "KY=" << crsysapi::Cnv::ToHex(key) << endl;
	cout << "PT=" << crsysapi::Cnv::ToHex(pt) << endl;
	ct = crsysapi::CipherStream::Bytes(pt, key, crsysapi::bvec_t(), crsysapi::CipherStream::Alg::Arcfour);
	cout << "CT=" << crsysapi::Cnv::ToHex(ct) << endl;
	assert(0 == crsysapi::Cnv::ToHex(ct).compare("75B7878099E0C596"));

	cout << "ChaCha20 test vector from \"ChaCha20 and Poly1305 for IETF protocols\"" << endl;
	key = crsysapi::Cnv::FromHex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");
	iv = crsysapi::Cnv::FromHex("000000000000004a00000000");
	cout << "KY=" << crsysapi::Cnv::ToHex(key) << endl;
	cout << "IV=" << crsysapi::Cnv::ToHex(iv) << endl;
	pt = crsysapi::str2bvec("Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.");
	cout << "PT=" << crsysapi::Cnv::ToHex(pt) << endl;
	ct = crsysapi::CipherStream::Bytes(pt, key, iv, crsysapi::CipherStream::Alg::Chacha20, 1);
	cout << "CT=" << crsysapi::Cnv::ToHex(ct) << endl;
	assert(0 == crsysapi::Cnv::ToHex(ct).compare("6E2E359A2568F98041BA0728DD0D6981E97E7AEC1D4360C20A27AFCCFD9FAE0BF91B65C5524733AB8F593DABCD62B3571639D624E65152AB8F530C359F0861D807CA0DBF500D6A6156A38E088A22B65E52BC514D16CCF806818CE91AB77937365AF90BBF74A35BE6B40B8EEDF2785E42874D"));

	cout << "Salsa20 test vector" << endl;
	key = crsysapi::Cnv::FromHex("80000000000000000000000000000000");
	iv = crsysapi::Cnv::FromHex("0000000000000000");
	cout << "KY=" << crsysapi::Cnv::ToHex(key) << endl;
	cout << "IV=" << crsysapi::Cnv::ToHex(iv) << endl;
	pt = crsysapi::bvec_t(64);	// 64 zero bytes
	std::fill(pt.begin(), pt.end(), 0);
	cout << "PT=" << crsysapi::Cnv::ToHex(pt) << endl;
	ct = crsysapi::CipherStream::Bytes(pt, key, iv, crsysapi::CipherStream::Alg::Salsa20);
	cout << "CT=" << crsysapi::Cnv::ToHex(ct) << endl;
	assert(0 == crsysapi::Cnv::ToHex(ct).compare("4DFA5E481DA23EA09A31022050859936DA52FCEE218005164F267CB65F5CFD7F2B4F97E0FF16924A52DF269515110A07F9E460BC65EF95DA58F740B7D1DBB0AA"));

	cout << "ChaCha20 test vector using files" << endl;
	key = crsysapi::Cnv::FromHex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");
	iv = crsysapi::Cnv::FromHex("000000000000004a00000000");
	cout << "KY=" << crsysapi::Cnv::ToHex(key) << endl;
	cout << "IV=" << crsysapi::Cnv::ToHex(iv) << endl;
	fileIn = "sunscreen.txt";
	fileOut = "sunscreen.chacha.dat";
	r = crsysapi::CipherStream::File(fileOut, fileIn, key, iv, crsysapi::CipherStream::Alg::Chacha20, 1);
	cout << "CipherStream::File returns " << r << " (expecting 0)" << endl;
	assert(0 == r);
	// Check file contents
	ct = read_binary_file(fileOut);
	cout << "File(CT)=" << crsysapi::Cnv::ToHex(ct) << endl;
	cout << "OK      =6E2E359A2568F98041BA0728DD0D6981E97E7AEC1D4360C20A27AFCCFD9FAE0BF91B65C5524733AB8F593DABCD62B3571639D624E65152AB8F530C359F0861D807CA0DBF500D6A6156A38E088A22B65E52BC514D16CCF806818CE91AB77937365AF90BBF74A35BE6B40B8EEDF2785E42874D" << endl;
}

void test_Aead() {
	crsysapi::bvec_t bv;
	crsysapi::bvec_t pt, ct, dt, key, nonce, data, aad;
	std::string okhex;

	cout << "\nTESTING Aead..." << endl;

	cout << "Ref: IEEE P802.1 MACsec 2.4.1 54-byte Packet Encryption Using GCM-AES-128:" << endl;
	key = crsysapi::Cnv::FromHex("071b113b 0ca743fe cccf3d05 1f737382");
	nonce = crsysapi::Cnv::FromHex("f0761e8d cd3d0001 76d457ed");
	aad = crsysapi::Cnv::FromHex("e20106d7 cd0df076 1e8dcd3d 88e54c2a 76d457ed");
	pt = crsysapi::Cnv::FromHex("08000f10 11121314 15161718 191a1b1c 1d1e1f20 21222324 25262728 292a2b2c 2d2e2f30 31323334 0004");
	okhex = "F0761E8DCD3D000176D457ED13B4C72B389DC5018E72A171DD85A5D3752274D3A019FBCAED09A425CD9B2E1C9B72EEE7C9DE7D52B3F3D6A5284F4A6D3FE22A5D6C2B960494C3";
	cout << "K: " << crsysapi::Cnv::ToHex(key) << endl;
	cout << "N: " << crsysapi::Cnv::ToHex(nonce) << endl;
	cout << "A: " << crsysapi::Cnv::ToHex(aad) << endl;
	cout << "P: " << crsysapi::Cnv::ToHex(pt) << endl;
	ct = crsysapi::Aead::EncryptWithTag(pt, key, nonce, aad, crsysapi::Aead::Alg::Aes_128_Gcm, crsysapi::Aead::Opts::PrefixIV);
	cout << "Output prefixed with IV and with tag appended..." << endl;
	cout << "C: " << crsysapi::Cnv::ToHex(ct) << endl;
	cout << "OK " << okhex << endl;
	assert(0 == crsysapi::Cnv::ToHex(ct).compare(okhex));
	// Now decrypt
	okhex = crsysapi::Cnv::ToHex(pt);
	dt = crsysapi::Aead::DecryptWithTag(ct, key, nonce, aad, crsysapi::Aead::Alg::Aes_128_Gcm, crsysapi::Aead::Opts::PrefixIV);
	cout << "P: " << crsysapi::Cnv::ToHex(dt) << endl;
	cout << "OK " << okhex << endl;
	assert(0 == crsysapi::Cnv::ToHex(dt).compare(okhex));


	cout << "Ref: RFC7739 ChaCha20_Poly1305 Appendix A.5:" << endl;
	key = crsysapi::Cnv::FromHex("1c9240a5eb55d38af333888604f6b5f0473917c1402b80099dca5cbc207075c0");
	nonce = crsysapi::Cnv::FromHex("000000000102030405060708");
	aad = crsysapi::Cnv::FromHex("f33388860000000000004e91");
	pt = crsysapi::Cnv::FromHex("496e7465726e65742d4472616674732061726520647261667420646f63756d656e74732076616c696420666f722061206d6178696d756d206f6620736978206d6f6e74687320616e64206d617920626520757064617465642c207265706c616365642c206f72206f62736f6c65746564206279206f7468657220646f63756d656e747320617420616e792074696d652e20497420697320696e617070726f70726961746520746f2075736520496e7465726e65742d447261667473206173207265666572656e6365206d6174657269616c206f7220746f2063697465207468656d206f74686572207468616e206173202fe2809c776f726b20696e2070726f67726573732e2fe2809d");
	// Expected output is ciphertext + 16-byte tag
	okhex = "64A0861575861AF460F062C79BE643BD5E805CFD345CF389F108670AC76C8CB24C6CFC18755D43EEA09EE94E382D26B0BDB7B73C321B0100D4F03B7F355894CF332F830E710B97CE98C8A84ABD0B948114AD176E008D33BD60F982B1FF37C8559797A06EF4F0EF61C186324E2B3506383606907B6A7C02B0F9F6157B53C867E4B9166C767B804D46A59B5216CDE7A4E99040C5A40433225EE282A1B0A06C523EAF4534D7F83FA1155B0047718CBC546A0D072B04B3564EEA1B422273F548271A0BB2316053FA76991955EBD63159434ECEBB4E466DAE5A1073A6727627097A1049E617D91D361094FA68F0FF77987130305BEABA2EDA04DF997B714D6C6F2C29A6AD5CB4022B02709B"
		"EEAD9D67890CBB22392336FEA1851F38";
	cout << "K: " << crsysapi::Cnv::ToHex(key) << endl;
	cout << "N: " << crsysapi::Cnv::ToHex(nonce) << endl;
	cout << "A: " << crsysapi::Cnv::ToHex(aad) << endl;
	cout << "P: " << crsysapi::Cnv::ToHex(pt) << endl;
	ct = crsysapi::Aead::EncryptWithTag(pt, key, nonce, aad, crsysapi::Aead::Alg::Chacha20_Poly1305);
	cout << "C: " << crsysapi::Cnv::ToHex(ct) << endl;
	cout << "OK " << okhex << endl;
	assert(0 == crsysapi::Cnv::ToHex(ct).compare(okhex));
	// Now decrypt
	okhex = crsysapi::Cnv::ToHex(pt);
	dt = crsysapi::Aead::DecryptWithTag(ct, key, nonce, aad, crsysapi::Aead::Alg::Chacha20_Poly1305);
	cout << "P: " << crsysapi::Cnv::ToHex(dt) << endl;
	cout << "OK " << okhex << endl;
	assert(0 == crsysapi::Cnv::ToHex(dt).compare(okhex));
	cout << crsysapi::bvec2str(dt) << endl;
}

void test_Wipe() {
	crsysapi::bvec_t bv;
	std::string s;
	cout << "\nTESTING Wipe..." << endl;
	bv = crsysapi::str2bvec("Some secret data");
	crsysapi::Wipe::Data(bv);
	cout << "After Wipe::Data='" << crsysapi::Cnv::ToHex(bv) << "'" << endl;
	s = "More secret data";
	crsysapi::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;
	crsysapi::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";
	crsysapi::bvec_t salt = crsysapi::Cnv::FromHex("78 57 8E 5A 5D 63 CB 06");
	int count = 2048;
	crsysapi::bvec_t dk;
	dk = crsysapi::Pbe::Kdf2(24, password, salt, count);
	cout << "dk=" << crsysapi::Cnv::ToHex(dk) << endl;
	assert(dk == crsysapi::Cnv::FromHex("BFDE6BE94DF7E11DD409BCE20A0255EC327CB936FFE93643"));
	// Same params but derive longer key (CAUTION: never use same salt in practice)
	dk = crsysapi::Pbe::Kdf2(64, password, salt, count);
	cout << "dk=" << crsysapi::Cnv::ToHex(dk) << endl;
	assert(dk == crsysapi::Cnv::FromHex("BFDE6BE94DF7E11DD409BCE20A0255EC327CB936FFE93643C4B150DEF77511224479994567F2E9B4E3BD0DF7AEDA3022B1F26051D81505C794F8940C04DF1144"));
	// Use different HMAC algorithm
	dk = crsysapi::Pbe::Kdf2(24, password, salt, count, crsysapi::Pbe::PrfAlg::Hmac_Sha256);
	cout << "dk=" << crsysapi::Cnv::ToHex(dk) << endl;
	assert(dk == crsysapi::Cnv::FromHex("97B5A91D35AF542324881315C4F849E327C4707D1BC9D322"));
}

void test_Crc() {
	cout << "\nTESTING CRC..." << endl;
	int n;
	std::string s = "123456789";
	n = crsysapi::Crc::String(s);
	std::cout << std::hex << n << std::dec << std::endl;
	assert(n = 0xCBF43926);
	crsysapi::bvec_t bv = crsysapi::str2bvec("123456789");
	n = crsysapi::Crc::Bytes(bv);
	std::cout << std::hex << n << std::dec << std::endl;
	assert(n = 0xCBF43926);
	n = crsysapi::Crc::File("1-9.txt");
	std::cout << std::hex << n << std::dec << std::endl;
	assert(n = 0xCBF43926);

	cout << "EXPECTING ERROR: Missing file..." << endl;
	try {
		n = crsysapi::Crc::File("missing.file");
		std::cout << std::hex << n << std::dec << std::endl;
	}
	catch (std::exception& e) {
		cout << e.what() << endl;
	}
}

void test_Xof() {
	cout << "\nTESTING XOF..." << endl;
	crsysapi::bvec_t bv;
	crsysapi::bvec_t msg = crsysapi::Cnv::FromHex("6ae23f058f0f2264a18cd609acc26dd4dbc00f5c3ee9e13ecaea2bb5a2f0bb6b");
	// Output 2000 bits
	bv = crsysapi::Xof::Bytes(2000 / 8, msg, crsysapi::Xof::XofAlg::Shake256);
	cout << "OUT=" << crsysapi::Cnv::ToHex(bv) << endl;
	assert(crsysapi::Cnv::ToHex(bv) == 
		"B9B92544FB25CFE4EC6FE437D8DA2BBE00F7BDAFACE3DE97B8775A44D753C3ADCA3F7C6F183CC8647E229070439AA9539AE1F8F13470C9D3527FFFDEEF6C94F9F0520FF0C1BA8B16E16014E1AF43AC6D94CB7929188CCE9D7B02F81A2746F52BA16988E5F6D93298D778DFE05EA0EF256AE3728643CE3E29C794A0370E9CA6A8BF3E7A41E86770676AC106F7AE79E67027CE7B7B38EFE27D253A52B5CB54D6EB4367A87736ED48CB45EF27F42683DA140ED3295DFC575D3EA38CFC2A3697CC92864305407369B4ABAC054E497378DD9FD0C4B352EA3185CE1178B3DC1599DF69DB29259D4735320C8E7D33E8226620C9A1D22761F1D35BDFF79A");
}

void test_Prf() {
	cout << "\nTESTING PRF..." << endl;
	crsysapi::bvec_t bv;
	crsysapi::bvec_t key = crsysapi::Cnv::FromHex("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F");
	crsysapi::bvec_t msg = crsysapi::Cnv::FromHex("00010203");
	bv = crsysapi::Prf::Bytes(256 / 8, msg, key, crsysapi::Prf::PrfAlg::Kmac128, "My Tagged Application");
	cout << "OUT=" << crsysapi::Cnv::ToHex(bv) << endl;
	assert(crsysapi::Cnv::ToHex(bv) == "3B1FBA963CD8B0B59E8C1A6D71888B7143651AF8BA0A7070C0979E2811324AA5");
}


void test_Err() {
	cout << "\nTESTING ERR..." << endl;
	for (int i = 0; i < 7; i++) {
		// Print message for every third error code, just to demonstrate
		int errCode = i * 3;
		cout << "ErrorLookup(" << errCode << ")=" << crsysapi::Err::ErrorLookup(errCode) << endl;
	}
	cout << crsysapi::Err::FormatErrorMessage(1) << endl;


}

void do_all() {
	test_Cnv();
	test_Rng();
	test_Hash();
	test_Hash_Obj();
	test_Mac();
	test_Mac_Obj();
	test_Cipher();
	test_Cipher_Obj();
	test_CipherStream();
	test_Aead();
	test_Wipe();
	test_Pbe();
	test_Crc();
	test_Xof();
	test_Prf();
	test_Err();
}

int main(int argc, char *argv[])
{
#ifdef _WIN32
	// Set Windows console display page to UTF-8
	SetConsoleOutputCP(CP_UTF8);
#endif
	cout << "Compiled: " __DATE__ " " __TIME__ << endl;
	cout << "__cplusplus=" << __cplusplus << endl;
	cout << "Gen::Version=" << crsysapi::Gen::Version() << endl;
	cout << "Gen::CompileTime=" << crsysapi::Gen::CompileTime() << endl;
	cout << "Gen::ModuleName=" << crsysapi::Gen::ModuleName() << endl;
	cout << "Gen::Platform=" << crsysapi::Gen::Platform() << endl;

	if (argc > 1 && strcmp(argv[1], "all") == 0) {
		cout << "DOING ALL..." << endl;
		do_all();
	}
	else {
		cout << "DOING SOME..." << endl;

		//test_Cnv();
		//test_Rng();
		//test_Hash();
		test_Hash_Obj();
		//test_Mac();
		//test_Mac_Obj();
		//test_Cipher();
		//test_Cipher_Obj();
		//test_CipherStream();
		//test_Aead();
		//test_Wipe();
		//test_Pbe();
		//test_Crc();
		//test_Xof();
		//test_Prf();
		//test_Err();
	}

	// ******************************************
	// FINALLY, DISPLAY QUICK INFO ABOUT THE CORE DLL
	cout << "\nFINALLY, DETAILS OF CORE NATIVE DLL..." << endl;
	cout << "DLL Version=" << crsysapi::Gen::Version() << " [" << crsysapi::Gen::Platform() << "] Lic={"
		<< crsysapi::Gen::LicenceType() << "} Compiled=[" << crsysapi::Gen::CompileTime() << "] " << endl;
	cout << "[" << crsysapi::Gen::ModuleName() << "]" << endl;

	cout << endl << "ALL DONE." << endl;

}