1
0
forked from cheng/wallet
wallet/src/unit_test.cpp

1105 lines
46 KiB
C++
Raw Normal View History

#include "stdafx.h"
using ro::msec, ro::msec_since_epoch, ro::bin2hex, ro::to_base64_string, ristretto255::hash,
ristretto255::hsh, ristretto255::scalar,
ristretto255::point, ro::base58;
class FailureToThrowExpectedException : public MyException {
public:
using MyException::MyException;
FailureToThrowExpectedException() noexcept :
MyException(FailureToThrowExpectedException_sz) {}
};
class EndUnitTestOfExceptions : public MyException {
public:
using MyException::MyException;
EndUnitTestOfExceptions() noexcept :
MyException("\t\tEnd unit test") {}
};
int errorCode{ 0 };
std::string szError;
static bool StartUnitTest(void);
static bool unexpected_unit_test(void);
static bool(*unit_test_action)(void) { &StartUnitTest };
static bool (*next_action)(void) { &unexpected_unit_test };
static bool noaction() { return false; };
static bool EndUnitTest(void);
static bool unexpected_unit_test() {
unit_test_action = &EndUnitTest;
next_action = unit_test_action;
szError = "Faulty flow of control in unit test itself";
ILogError(szError.c_str());
errorCode = 1005;
return true;
}
class UnitTestModalHook : public wxModalDialogHook
{
protected:
virtual int Enter(wxDialog* dlg) wxOVERRIDE
{
wxLogMessage(
wxT("Showing %s:%s dialog"),
dlg->GetClassInfo()->GetClassName(),
dlg->GetLabel()
);
auto x = dlg->GetId();
switch (x) {
case myID_ERRORMESSAGE:
wxLogMessage(wxT("\tError message modal dialog"));
unit_test_action = next_action;
wxLogMessage(wxT("\tClosing dialog"));
return wxID_OK;
default:
return wxID_NONE;
}
}
};
static UnitTestModalHook modal_dialog_hook;
static bool EndUnitTest() {
unit_test_action = &noaction;
modal_dialog_hook.Unregister();
next_action = &unexpected_unit_test;
static std::string intestbed("Testbed: ");
try {
testbed::testbed();
throw EndUnitTestOfExceptions();
}
catch (const EndUnitTestOfExceptions&) {}
catch (const std::exception& e) {
errorCode = 1001;
szError = intestbed + e.what();
ILogError(szError.c_str());
}
catch (...) {
szError = intestbed + sz_unknown_error;
errorCode = 1000;
ILogError(szError.c_str());
}
if (errorCode == 0) {
ILogMessage("Passed Unit Test");
}
else {
wxLogMessage(wxT("\nFailed Unit Test\nunit test error %d\n%s"),
errorCode, _wx(szError.c_str())
);
}
if (singletonApp->m_unit_test && !singletonApp->m_display) {
singletonFrame->Close(); //Shuts down program immediately.
// When the program is closed, now or later, will return errorCode.
}
return true;
}
static bool curve_order(void) {
unit_test_action = &EndUnitTest;
next_action = unit_test_action;
ILogMessage(
"\tChecking mpz arbitrary precision integer conversions to cryptographic values."
);
try {
scalar max(-1);
wxString curve_order_as_unreduced_scalar{ _wx(bin2hex(scalar::scOrder)) };
ro::CompileSizedString<sizeof(scalar) * 2> sz;
mpz_get_str(sz, 16, mpir::ristretto25519_curve_order);
wxString curve_order_as_mpir_bigint{ _wx(sz) };
if (curve_order_as_unreduced_scalar != curve_order_as_mpir_bigint){
throw MyException("inconsistent curve order");
}
scalar sc_zero{ 0 };
scalar sc_one{ 1 };
scalar sc1{ scalar::random() };
scalar sc2{ scalar::random() };
assert(sc1 != sc2);
assert(sc1<sc2 || sc1>sc2);
assert(sc1 < scalar::scOrder);
assert(max < scalar::scOrder);
assert(max > sc_zero);
assert(max + sc_one == scalar(0));
assert(sc_one + max < scalar::scOrder);
assert(!(sc_one < scalar::scOrder + sc_one));
assert(sc_one == scalar::scOrder + sc_one);
assert((sc_one + max < scalar::scOrder));
assert(sc1.valid());
assert(!scalar::scOrder.valid());
assert(!scalar(0).valid());
bool f = true
&& sc1 != sc2
&& sc1<sc2 || sc1>sc2
&& (sc1 < scalar::scOrder
&& max < scalar::scOrder
&& max > sc_zero
&& max + sc_one == sc_zero
&& sc_one + max < scalar::scOrder
&& !(sc_one < scalar::scOrder + sc_one)
&& sc_one == scalar::scOrder + sc_one
&& (sc_one + max < scalar::scOrder)
&& sc1.valid())
&& !scalar::scOrder.valid()
&& !scalar(0).valid();
if (!f)throw MyException("scalar range and validation messed up");
}
catch (const std::exception& e) {
errorCode = 30;
szError = e.what();
ILogError(szError.c_str());
}
catch (...) {
szError = sz_unknown_error;
errorCode = 30;
ILogError(szError.c_str());
}
return true;
}
static bool checkDataConversionsProduceExpected(void){
unit_test_action = &curve_order;
next_action = unit_test_action;
ILogMessage("\tChecking hashing, string conversion, scalar and point generation.");
try {
char brownfox[]{ "the quick brown fox jumped over the lazy dog" };
char* p_brownfox{ brownfox };
std::array<char, 27>ar_brownfox{ "the quick brown fox jumped" };
char* p_over{ p_brownfox + 27 };
wxString wx_over{ p_over };
*--ar_brownfox.end() = ' '; //fixes the trailing nul appended to ar_brownfox, so that it is
// no longer a null terminated string.
wxString wx_jumped(&ar_brownfox[0], std::size(ar_brownfox));
hsh hsh1;
hsh hsh2;
hsh1 << brownfox;
hsh2 << ar_brownfox << wx_over.ToUTF8();
hash hash1(hsh1);
hash hash2(hsh2);
hash hash3(hsh() << ar_brownfox << wx_over.ToUTF8());
if (false
|| hash1 != hash2
|| hash1 != hash(p_brownfox)
|| hash1 != hash3
|| hash(p_brownfox) != hash(hsh() << ar_brownfox << wx_over.ToUTF8())
|| hash(p_brownfox) != hash(ar_brownfox, wx_over.ToUTF8())
|| hash(brownfox) != hash(ar_brownfox, p_over)
)throw MyException(sz_inconsistent_hash);
{
scalar scl_b{ scalar::random() };
point pt_a{ scl_b.timesBase() };
std::string str_pt_a{ &(base58(pt_a))[0] };
if (pt_a != base58<point>::bin(str_pt_a.c_str())){
MyException("Round trip from and two base 58 representation failed");
}
}
{
point pt_b;
try {
pt_b = base58<point>::bin(
"galvhAmKLYPjiEUbiDvE54pnKhhdYSxC3G5p9czHPuKaB not base 58 because uses 'l'"
);
throw FailureToThrowExpectedException();
assert(false);
}
catch (const NotBase58Exception&) {}
try {
pt_b = base58<point>::bin(
"AWyxo34SopBAtWy9qH6RfheK3xUCmu9fgF4ZjHwT9RVrS wrong checksum"
);
throw FailureToThrowExpectedException();
assert(false);
}
catch (const BadStringRepresentationOfCryptoIdException&) {}
try {
pt_b = base58<point>::bin(
"MaevYgZimDjzQWxbnHcNdMqkMVmjWpYEqippTfj9y6U7sM overlong error"
);
throw FailureToThrowExpectedException();
assert(false);
}
catch (const OversizeBase58String&) {}
}
hash<512>hash_a{ ar_brownfox, wx_over.ToUTF8(), hash1,base58<point>::bin(
"AWyxo34SopBAtWy9qH6RfheK3xUCmu9fgF4ZjHwT9RVrP"
) };
std::string str_hash_a{ &(base58(hash_a))[0] };
scalar scl_a{ hash_a };
std::string str_sclr_a = &(base58(scl_a))[0];
assert(base58<scalar>::bin(str_sclr_a.c_str()) == scl_a);
point pt_a{ scl_a.timesBase() };
std::string str_pt_a = &(base58(pt_a))[0];
assert(base58<point>::bin(str_pt_a.c_str()) == pt_a);
hash<256> hash_b{ hash_a,str_hash_a,scl_a,str_sclr_a,pt_a,str_pt_a,33, 66ull };
if (base58(hash_b).operator std::string() !=
"i22EVNPsKRjdxYTZrPPu9mx6vnrBjosFix5F4gn2mb2kF"
){
throw MyException("unexpected hash of transformations");
}
}
catch (const std::exception& e) {
errorCode = 27;
szError = e.what();
ILogError(szError.c_str());
}
catch (...) {
szError = sz_unknown_error;
errorCode = 26;
ILogError(szError.c_str());
}
return true;
}
static bool CheckForUtfEnvironment(void) {
ILogMessage("\tChecking for UTF locale.");
try {
bool utfEnvironment{ true };
wxString utfError{ wxT("") };
if constexpr (b_WINDOWS) {
auto ACP{ GetACP() };
utfEnvironment = utfEnvironment && (ACP == 65001);
if (!utfEnvironment) {
utfError += wxString::Format(wxT("current code page %d—should be 65001☹, "), ACP);
}
}
auto FontEncoding{ wxLocale::GetSystemEncoding() };
utfEnvironment = utfEnvironment && (false
|| (FontEncoding == wxFONTENCODING_UTF8)
|| (FontEncoding == wxFONTENCODING_UTF16BE)
|| (FontEncoding == wxFONTENCODING_UTF16LE)
|| (FontEncoding == wxFONTENCODING_UTF32BE)
|| (FontEncoding == wxFONTENCODING_UTF32LE)
|| (FontEncoding == wxFONTENCODING_SYSTEM)
);
if (!utfEnvironment) {
utfError = wxString::Format(wxT("%swxFontEncoding %d—should be %d☹"),
utfError,
FontEncoding,
wxFONTENCODING_UTF8);
wxLogMessage(wxT("%s"), utfError);
}
if (!utfEnvironment) { throw MyException(utfError); }
}
catch (const std::exception& e) {
errorCode = 24;
szError = e.what();
ILogError(szError.c_str());
}
catch (...) {
szError = sz_unknown_error;
errorCode = 24;
ILogError(szError.c_str());
}
unit_test_action = &checkDataConversionsProduceExpected;
return true;
}
static bool Waiting_for_delayed_dummy_error_message(void) {
static int count(12);
if ((--count)) {
ILogMessage("\t\tWaiting for second dummy error message");
}
else {
errorCode = 21;
szError = "Delayed dummy error message UI failed show up";
ILogError(szError.c_str());
unit_test_action = next_action;
}
return true;
}
class UnitTestOfException : public MyException {
public:
using MyException::MyException;
UnitTestOfException() noexcept :
MyException("\t\tNot a real error, merely at test of error handling UI") {}
};
static bool TestDelayedErrorDialog(void) {
unit_test_action = &Waiting_for_delayed_dummy_error_message;
next_action = &CheckForUtfEnvironment;
ILogMessage("\t\tthrowing exception");
throw UnitTestOfException(); //If we throw exception before setting the next unit test,
// this unit test gets endless recalled.
return true;
}
static bool Waiting_for_dummy_error_message(void) {
static int count(12);
if ((--count)) {
ILogMessage("\t\tWaiting for dummy error message");
}
else {
errorCode = 21;
szError = "Dummy error message UI failed show up";
ILogError(szError.c_str());
unit_test_action = next_action;
}
return true;
}
static bool TestErrorDialog(void) {
unit_test_action = &Waiting_for_dummy_error_message;
next_action= &TestDelayedErrorDialog;
ILogMessage("\tTest error handling UI.");
ILogMessage("\t\tposting delayed error message\n\t\tas if inside an exception handler or destructor");
auto event = new wxCommandEvent(wxEVT_MENU, myID_ERRORMESSAGE);
wxQueueEvent(singletonFrame->GetMenuBar(), event);
// wxQueueEvent(singletonApp, event);
unit_test_action = &Waiting_for_dummy_error_message;
return true;
}
static bool WalletCreationUI(void) {
// singletonFrame->menu_OnNewWallet.queue_event();
unit_test_action = &TestErrorDialog;
return true;
}
static bool OpenWallet(void) {
try {
/* If LastUsed is the empty string, check if default filename exists. If it exists, set
Last Used to default file name
If LastUsed is not the empty string, or no longer the empty string, attempt to open
indicated file. If open fails, or reading the opened file produces bad results, abort with
exception
If LastUsed is still the empty string, attempt to create default filename. If creation fails,
abort with exception. If it succeeds, set LastUsed to default filename.
The exception in unit test should simply generate an error message, but if run during
initialization, should bring up the more complex UI for constructing or selecting your
wallet file.*/
ILogMessage("\tWallet file");
assert(singletonApp->pConfig);
singletonApp->pConfig->SetPath(wxT("/Wallet"));
wxFileName LastUsedSqlite(singletonApp->pConfig->Read(wxT("LastUsed"), wxT("")));
bool fWalletNameOk{ false };
wxStandardPaths& StandardPaths(wxStandardPaths::Get());
StandardPaths.UseAppInfo(3);
wxFileName DefaultSqlite(StandardPaths.GetUserLocalDataDir(), "default.wallet");
wxLogMessage(wxT("\t\tLastUsed=\"%s\""), LastUsedSqlite.GetFullPath());
if (!LastUsedSqlite.IsOk() || !LastUsedSqlite.HasName() || !LastUsedSqlite.HasExt()) {
wxLogMessage(wxT("\t\tDefault=\"%s\""), DefaultSqlite.GetFullPath());
assert(DefaultSqlite.IsOk() && DefaultSqlite.HasName() && DefaultSqlite.HasExt());
if (DefaultSqlite.FileExists()) {
LastUsedSqlite = DefaultSqlite;
singletonApp->pConfig->Write(wxT("LastUsed"), DefaultSqlite.GetFullPath());
fWalletNameOk = true;
}
}
else fWalletNameOk = true;
std::unique_ptr<ISqlite3> db;
if (fWalletNameOk) {
if (!LastUsedSqlite.FileExists()) throw MyException("Expected wallet file not found");
db.reset(Sqlite3_open(LastUsedSqlite.GetFullPath().ToUTF8()));
sql_read_from_misc read_from_misc(db.get());
if(!read_from_misc(1) || read_from_misc.value<int64_t>() != WALLET_FILE_IDENTIFIER)throw MyException("Unrecognizable wallet file format");
if(!read_from_misc(2) || read_from_misc.value<int64_t>() != WALLET_FILE_SCHEMA_VERSION_0_0)throw MyException("Unrecognized wallet schema");
if (!read_from_misc(4)) throw MyException("Mastersecret missing");
ristretto255::CMasterSecret MasterSecret(*read_from_misc.value<ristretto255::scalar>());
ro::sql read_keys(db.get(), R"|(SELECT * FROM "Keys" LIMIT 5;)|");
sql_read_name read_name(db.get());
// db.reset(nullptr);// Force error of premature destruction of Isqlite3
while (read_keys.step() == Icompiled_sql::ROW) {
auto pubkey = read_keys.column<ristretto255::point>(0);
auto id = read_keys.column<int>(1);
auto use = read_keys.column<int>(2);
if (use != 1){
throw MyException(R"|(Unknown secret key algorithm index in "Names" table)|");
}
if (!read_name(id)){
throw MyException(R"|(No entry corresponding to public key in "Names" table)|");
}
const char* name = read_name.name();
if(MasterSecret(name).timesBase()!=*pubkey)throw MyException(R"|(Public key of name fails to correspond)|");
wxLogMessage(wxT("\t\t\"%s\" has expected public key 0x%s"), name, (wxString)(bin2hex(*pubkey)));
}
}
else {
// At this point in the code the filename LastUsedSqlite is a bad filename, normally the empty string, and the default wallet file does not exist in the default location.
// Construct default wallet and filename*/
wxFileName path{ StandardPaths.GetUserLocalDataDir() };
try {
// Disk operations to create wallet, which may throw.
// This try/catch block exists to catch disk io issues.
if (!path.DirExists())path.Mkdir();
if (!DefaultSqlite.DirExists())DefaultSqlite.Mkdir();
db.reset(Sqlite3_create(DefaultSqlite.GetFullPath().ToUTF8()));
db->exec(R"|(
PRAGMA journal_mode = WAL;
PRAGMA synchronous = 1;
BEGIN IMMEDIATE TRANSACTION;
CREATE TABLE "Keys"(
"pubkey" BLOB NOT NULL UNIQUE PRIMARY KEY,
"id" integer NOT NULL,
"use" INTEGER NOT NULL);
CREATE TABLE "Names"(
"name" TEXT NOT NULL UNIQUE
);
CREATE TABLE "Misc"(
"index" INTEGER NOT NULL UNIQUE PRIMARY KEY,
"m" BLOB
);
COMMIT;)|");
LastUsedSqlite = DefaultSqlite;
singletonApp->pConfig->Write(wxT("LastUsed"), DefaultSqlite.GetFullPath());
wxLogMessage(wxT("\t\tConstructing default wallet %s"), DefaultSqlite.GetFullPath());
// We now have a working wallet file with no valid data. Attempting to create a strong random secret, a name, and public and private keys for that name.
wxLogMessage(wxT("\t\tGenerating random 128 bit wallet secret"));
auto text_secret{ DeriveTextSecret(ristretto255::scalar::random(), 1) };
ro::msec start_time{ ro::msec_since_epoch() };
ristretto255::CMasterSecret MasterSecret(DeriveStrongSecret(&text_secret[0]) );
decltype(start_time) end_time{ ro::msec_since_epoch() };
wxLogMessage(wxT("\t\tStrong secret derivation took %d milliseconds"), (end_time - start_time).count());
sql_update_to_misc update_to_misc(db.get());
update_to_misc(1, WALLET_FILE_IDENTIFIER);
update_to_misc(2, WALLET_FILE_SCHEMA_VERSION_0_0);
update_to_misc(3, &text_secret[0]);
update_to_misc(4, MasterSecret);
sql_insert_name insert_name(db.get());
const char cpsz[]{ "Unit Tester" };
insert_name(cpsz, MasterSecret(cpsz).timesBase());
}
catch (const MyException & e) {
ILogError(R"|(Failed to create or failed to properly initialize wallet)|");
errorCode = 20;
szError = e.what();
ILogError(szError.c_str());
}
} // End of wallet creation branch
}
catch (const std::exception & e) {
errorCode = 20;
szError = e.what();
ILogError(szError.c_str());
}
catch (...) {
szError = sz_unknown_error;
errorCode = 20;
ILogError(szError.c_str());
}
unit_test_action = &WalletCreationUI;
return true;
}
static bool StandardPaths(void) {
wxStandardPaths& StandardPaths{ wxStandardPaths::Get() };
StandardPaths.UseAppInfo(3);
ILogMessage("\tStandard paths");
wxLogMessage("\t\twxStandardPaths::GetUserLocalDataDir()\t %s", StandardPaths.GetUserLocalDataDir());
wxLogMessage("\t\twxStandardPaths::GetUserDataDir() \t %s", StandardPaths.GetUserDataDir());
wxLogMessage("\t\twxStandardPaths::GetLocalDataDir() \t %s", StandardPaths.GetLocalDataDir());
unit_test_action = &OpenWallet;
return true;
}
static bool TestErrorHandlingUI(void) {
next_action = unit_test_action = &StandardPaths;
throw UnitTestOfException(); //If we throw exception before setting the next unit test, this unit test gets endless recalled.
return true;
}
static uintmax_t shared_secrets_per_second{ 0 };
namespace ristretto255 {
static bool TestSecretGeneration(void) {
try {
ILogMessage("\tTest pseudorandom generation of secrets from scalar(7).");
auto text_secret{ DeriveTextSecret(scalar(7), 1) };
if (text_secret == decltype(text_secret){"Rmc mLSu mDk DhfV 9gBK kKj"}) {
ILogMessage("\t\tObtained expected text secret from scalar(7)");
}
else {
errorCode = 18;
szError = "Fail\tUnexpected text secret from scalar(7)";
ILogError(szError.c_str());
}
if (std::popcount(0x0123456789ABCDEFu) != 0x20)throw MyException(sz_bitcount_incorrect);
if (rounded_log2(0x345678u) != 22) throw MyException(sz_rounded_log2_incorrect);
{ uint64_t v{ 0x123456789ABCDEFu };
for (unsigned int i = 0; i <= 64; i++) {
if (i != std::countr_zero(v))throw MyException(sz_count_of_trailing_zero_bits_incorrect);
v <<= 1;
}
}
if (!(singletonApp->m_quick_unit_test)){
auto start_time{ std::chrono::high_resolution_clock::now() };
auto s1{ DeriveStrongSecret(&text_secret[0]) };
auto end_time{ std::chrono::high_resolution_clock::now() };
if (s1 == scalar({ 0x59, 0xf6, 0x73, 0xb4, 0xa0, 0xc7, 0x0d, 0x8b, 0x51, 0xe5, 0x87, 0x7c, 0xf5, 0xd7, 0x6f, 0x55, 0x31, 0xa7, 0x0b, 0x14, 0x28, 0x54, 0x97, 0x08, 0x9f, 0x27, 0x83, 0xe1, 0xc7, 0x5f, 0x55, 0x0f })) {
wxLogMessage("\t\tObtained expected strong scalar secret from scalar(7)");
}
else {
errorCode = 17;
szError = "Fail\tUnexpected strong scalar secret from text secret";
ILogError(szError.c_str());
}
auto time_taken{ std::chrono::duration_cast<std::chrono::microseconds> (end_time - start_time) };
wxLogMessage("\t\tStrong secret derivation took %lld microseconds", time_taken.count());
uintmax_t strengthening = time_taken.count() * shared_secrets_per_second / 1000000;
wxLogMessage("\t\tslows brute force password search by a factor of %ju", strengthening);
wxLogMessage("\t\tstrengthens password by %u bits",
static_cast<unsigned int>(rounded_log2(static_cast<uint32_t>(strengthening))));
auto s2{ DeriveSecret(s1, 1) };
if (s2 == scalar({ 0x02, 0x98, 0x0e, 0x7d, 0x12, 0xe8, 0x23, 0xeb, 0xa2, 0x08, 0xa8, 0x51, 0x73, 0x27, 0x96, 0xc6, 0xb9, 0x12, 0x60, 0x27, 0x25, 0x24, 0x75, 0xa3, 0x4e, 0x2a, 0x6a, 0x44, 0x56, 0xab, 0x95, 0x06 })) {
ILogMessage("\t\tObtained expected fast scalar secret from strong scalar secret");
}
else {
errorCode = 16;
szError = "Fail\tUnexpected fast scalar secret from strong scalar secret";
ILogError(szError.c_str());
}
}
else {
ILogMessage("\t\tSkipping test of strong secret generation in quick unit test, because strong secret generation is deliberately slow.");
}
}
catch (const std::exception & e) {
errorCode = 16;
szError = e.what();
ILogError(szError.c_str());
}
catch (...) {
szError = sz_unknown_error;
errorCode = 16;
ILogError(szError.c_str());
}
unit_test_action = &TestErrorHandlingUI;
return true;
}
static bool TestShareSecretGenerationSpeed(void) {
ILogMessage("\tTest shared secret generation speed.");
try {
// throw MyException("fake failure to test failure handling");
unsigned int secrets{ 10 };
scalar a{ scalar::random() };
scalar b{ scalar::random() };
scalar c;
auto B{ a.timesBase() };
decltype(B) A, C;
auto start_time{ std::chrono::high_resolution_clock::now() };
for (unsigned int i{ secrets }; i; i--) {
c = a + b;
C = c * B;
a = b + c;
A = a * C;
b = c + a;
B = b * A;
}
auto end_time{ std::chrono::high_resolution_clock::now() };
if (!B.valid()) { throw MyException("Unexpected invalid scalar"); }
auto time_taken{ std::chrono::duration_cast<std::chrono::microseconds> (end_time - start_time) };
wxLogMessage("\t\t%d shared secrets from public and private keys took %lld microseconds", secrets * 3, time_taken.count());
if (time_taken.count()) {
shared_secrets_per_second = secrets * 3000000 / time_taken.count();
wxLogMessage("\t\t in one second, can generate %ju shared secrets",
shared_secrets_per_second
);
}
}
catch (const std::exception& e) {
errorCode = 23;
szError = e.what();
ILogError(szError.c_str());
}
catch (...) {
szError = sz_unknown_error;
errorCode = 23;
ILogError(szError.c_str());
}
unit_test_action = &TestSecretGeneration;
return true;
}
bool operator ==(const std::span<byte>& p, const std::span<byte>& q) {
bool breturn{ true };
for (auto xq = q.begin(); auto xp:p) {
if (xp != *xq++) {
breturn = false;
break;
}
}
return breturn;
}
bool operator !=(const std::span<byte>& p, const std::span<byte>& q) {
bool breturn{ false };
for (auto xq = q.begin(); auto xp:p) {
if (xp != *xq++) {
breturn = true;
break;
}
}
return breturn;
}
static bool TestSignatures(void) {
try {
ILogMessage("\tTest Schnorr signatures.");
//Construct Ann's secret and public keys:
scalar sclrAnnSecretKey{ scalar::random() };
point ptAnnPublicKey{ sclrAnnSecretKey.timesBase() };
// Ann signs the string "hello"
std::array<char, 6> szHello{ "hello" };
// Ann creates signature.
signed_text sig(
sclrAnnSecretKey, // Signer's secret key
ptAnnPublicKey, // Signer's public key
szHello // Text to be signed.
);
auto start_time{ std::chrono::high_resolution_clock::now() };
if (sig.verify())
{
auto end_time{ std::chrono::high_resolution_clock::now() };
auto time_taken{ std::chrono::duration_cast<std::chrono::microseconds> (end_time - start_time) };
wxLogMessage("\t\tverification of Schnorr signature took %lld microseconds", time_taken.count());
ILogMessage("\t\tSchnorr Signature valid");
}
else {
errorCode = 9;
szError = "Fail\tInvalid signature.";
ILogError(szError.c_str());
}
// Testing signature vector, using a low, easily guessed, secret key
sig = signed_text(scalar(0x7F), scalar(0x7F).timesBase(), szHello);
assert(sig.verify());
scalar sig_test_vector(
{
0x7b, 0x28, 0xd4, 0x49, 0x99, 0xed, 0xb2, 0x40,
0x61, 0x4d, 0x22, 0x96, 0x25, 0x78, 0xb6, 0x3b,
0x5f, 0x72, 0xf7, 0xde, 0x99, 0x29, 0xe2, 0x50,
0x41, 0xa5, 0xcd, 0xaa, 0x5b, 0x0b, 0xff, 0x0d,
}
);
if (sig_test_vector == sig.c) {
ILogMessage("\t\tproduced expected signature test vector");
}
else {
errorCode = 14;
ILogMessage(("\t\tsig of test vector: " + to_base64_string(sig.c)).c_str());
ILogMessage(("\texpected sig of test vector: " + to_base64_string(sig_test_vector)).c_str());
szError = "Fail\tSignature different from expected test vector.";
ILogError(szError.c_str());
}
std::array<uint8_t, 6>expected_serialized_int{ 0x88,0x94,0x92,0x8d,0x88,0x45 };
bool f{ true };
// f = f && scalar(ro::deserialize<32>(ro::serialize(sig_test_vector))) == sig_test_vector;
// f = f && scalar(ro::deserialize<32>(&ro::serialize(sig_test_vector)[0])) == sig_test_vector;
f = f && ro::serialize(0x21U) == ro::serialize(int8_t(0x21));
f = f && ro::serialize(0x21) == ro::serialize(int8_t(0x21));
f = f && ro::serialize(-60) == ro::serialize(int8_t(-60));
f = f && ro::serialize(-8107) == ro::serialize(int16_t(-8107));
f = f && ro::serialize(0x41U).size() == 1;
f = f && ro::serialize(0x2142U).size() == 2;
f = f && ro::serialize(0x4142U).size() == 3;
f = f && ro::serialize(0x4142434445464748U).size() == 9;
f = f && ro::serialize(0x8142434445464748U).size() == 10;
f = f && ro::serialize(0x4142434445U) == expected_serialized_int;
f = f && ro::deserialize<uint64_t>(ro::serialize(0x4142434445U)) == 0x4142434445U;
f = f && ro::deserialize<uint64_t>(ro::serialize(0xFF41424344454647U)) == 0xFF41424344454647U;
f = f && ro::deserialize<int64_t>(ro::serialize(0x4142434445)) == 0x4142434445;
f = f && ro::deserialize<int64_t>(ro::serialize(0xFF41424344454647)) == 0xFF41424344454647;
f = f && ro::deserialize<int64_t>(ro::serialize(int32_t(-8107))) == -8107;
if (f) {
ILogMessage("\t\tro:serialize ro:deserialize on integers produced expected result");
}
else {
errorCode = 15;
szError = "Fail\tro:deserialize ro:deserialize on integers produced unexpected results.";
ILogError(szError.c_str());
}
wxLogMessage(wxT("\tLibsodium constants\n\t\tsizeof(crypto_secretstream_xchacha20poly1305_state)=%d"), int(sizeof(crypto_secretstream_xchacha20poly1305_state)));
wxLogMessage(wxT("\t\tcrypto_generichash_KEYBYTES=%d"), crypto_generichash_KEYBYTES);
wxLogMessage(wxT("\t\tcrypto_secretstream_xchacha20poly1305_KEYBYTES=%d"), crypto_secretstream_xchacha20poly1305_KEYBYTES);
wxLogMessage(wxT("\t\tcrypto_secretstream_xchacha20poly1305_HEADERBYTES=%d"), crypto_secretstream_xchacha20poly1305_HEADERBYTES);
wxLogMessage(wxT("\t\tchecksum size == crypto_secretstream_xchacha20poly1305_ABYTES ==%d"), crypto_secretstream_xchacha20poly1305_ABYTES);
wxLogMessage(wxT("\t\tcrypto_pwhash_OPSLIMIT_MIN ==\t%08x"), crypto_pwhash_OPSLIMIT_MIN);
wxLogMessage(wxT("\t\tcrypto_pwhash_OPSLIMIT_MODERATE ==\t%08x"), crypto_pwhash_OPSLIMIT_MODERATE);
wxLogMessage(wxT("\t\tcrypto_pwhash_OPSLIMIT_SENSITIVE ==\t%08x"), crypto_pwhash_OPSLIMIT_SENSITIVE);
wxLogMessage(wxT("\t\tcrypto_pwhash_OPSLIMIT_MAX ==\t%08x"), crypto_pwhash_OPSLIMIT_MAX);
wxLogMessage(wxT("\t\tcrypto_pwhash_MEMLIMIT_MIN ==\t%08x"), crypto_pwhash_MEMLIMIT_MIN);
wxLogMessage(wxT("\t\tcrypto_pwhash_MEMLIMIT_MODERATE==%08x"), crypto_pwhash_MEMLIMIT_MODERATE);
wxLogMessage(wxT("\t\tcrypto_pwhash_MEMLIMIT_SENSITIVE==\t%08x"), crypto_pwhash_MEMLIMIT_SENSITIVE);
wxLogMessage(wxT("\t\tcrypto_auth_BYTES ==\t\t%08x"), crypto_auth_BYTES);
wxLogMessage(wxT("\t\tcrypto_stream_chacha20_NONCEBYTES\t%08x"), crypto_stream_chacha20_NONCEBYTES);
wxLogMessage(wxT("\t\tcrypto_stream_chacha20_KEYBYTES ==\t%08x"), crypto_stream_chacha20_KEYBYTES);
wxLogMessage(wxT("\t\tchacha20 is 2^(256+128) 512 bit pseudo random blocks"));
wxLogMessage(wxT("\t\tcrypto_stream_xchacha20_NONCEBYTES\t%08x"), crypto_stream_xchacha20_NONCEBYTES);
wxLogMessage(wxT("\t\tcrypto_stream_xchacha20_KEYBYTES ==\t%08x"), crypto_stream_chacha20_KEYBYTES);
wxLogMessage(wxT("\t\txchacha20 is 2^512 512 bit pseudo random blocks"));
wxLogMessage(wxT("\t\tmin hash size is %d bits"), crypto_generichash_BYTES_MIN * 8);
wxLogMessage(wxT("\t\tmax hash size is %d bits"), crypto_generichash_BYTES_MAX * 8);
wxLogMessage(wxT("\t\tcrypto_shorthash_BYTES\t== %08x"), crypto_shorthash_BYTES);
wxLogMessage(wxT("\t\tcrypto_shorthash_KEYBYTES\t== %08x"), crypto_shorthash_KEYBYTES);
wxLogMessage(wxT("\t\tcrypto_auth_BYTES\t\t== %08x"), crypto_auth_BYTES);
}
catch (const std::exception & e) {
errorCode = 15;
szError = e.what();
ILogError(szError.c_str());
}
catch (...) {
szError = sz_unknown_error;
errorCode = 15;
ILogError(szError.c_str());
}
unit_test_action = &TestShareSecretGenerationSpeed;
return true;
}
static std::array<point, 0x10>ristretto_test_vector{
point({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }),
point({0xe2, 0xf2, 0xae, 0x0a, 0x6a, 0xbc, 0x4e, 0x71, 0xa8, 0x84, 0xa9, 0x61, 0xc5, 0x00, 0x51, 0x5f, 0x58, 0xe3, 0x0b, 0x6a, 0xa5, 0x82, 0xdd, 0x8d, 0xb6, 0xa6, 0x59, 0x45, 0xe0, 0x8d, 0x2d, 0x76}),
point({ 0x6a, 0x49, 0x32, 0x10, 0xf7, 0x49, 0x9c, 0xd1, 0x7f, 0xec, 0xb5, 0x10, 0xae, 0x0c, 0xea, 0x23, 0xa1, 0x10, 0xe8, 0xd5, 0xb9, 0x01, 0xf8, 0xac, 0xad, 0xd3, 0x09, 0x5c, 0x73, 0xa3, 0xb9, 0x19}),
point({0x94, 0x74, 0x1f, 0x5d, 0x5d, 0x52, 0x75, 0x5e, 0xce, 0x4f, 0x23, 0xf0, 0x44, 0xee, 0x27, 0xd5, 0xd1, 0xea, 0x1e, 0x2b, 0xd1, 0x96, 0xb4, 0x62, 0x16, 0x6b, 0x16, 0x15, 0x2a, 0x9d, 0x02, 0x59}),
point({0xda, 0x80, 0x86, 0x27, 0x73, 0x35, 0x8b, 0x46, 0x6f, 0xfa, 0xdf, 0xe0, 0xb3, 0x29, 0x3a, 0xb3, 0xd9, 0xfd, 0x53, 0xc5, 0xea, 0x6c, 0x95, 0x53, 0x58, 0xf5, 0x68, 0x32, 0x2d, 0xaf, 0x6a, 0x57}),
point({0xe8, 0x82, 0xb1, 0x31, 0x01, 0x6b, 0x52, 0xc1, 0xd3, 0x33, 0x70, 0x80, 0x18, 0x7c, 0xf7, 0x68, 0x42, 0x3e, 0xfc, 0xcb, 0xb5, 0x17, 0xbb, 0x49, 0x5a, 0xb8, 0x12, 0xc4, 0x16, 0x0f, 0xf4, 0x4e}),
point({0xf6, 0x47, 0x46, 0xd3, 0xc9, 0x2b, 0x13, 0x05, 0x0e, 0xd8, 0xd8, 0x02, 0x36, 0xa7, 0xf0, 0x00, 0x7c, 0x3b, 0x3f, 0x96, 0x2f, 0x5b, 0xa7, 0x93, 0xd1, 0x9a, 0x60, 0x1e, 0xbb, 0x1d, 0xf4, 0x03}),
point({0x44, 0xf5, 0x35, 0x20, 0x92, 0x6e, 0xc8, 0x1f, 0xbd, 0x5a, 0x38, 0x78, 0x45, 0xbe, 0xb7, 0xdf, 0x85, 0xa9, 0x6a, 0x24, 0xec, 0xe1, 0x87, 0x38, 0xbd, 0xcf, 0xa6, 0xa7, 0x82, 0x2a, 0x17, 0x6d}),
point({0x90, 0x32, 0x93, 0xd8, 0xf2, 0x28, 0x7e, 0xbe, 0x10, 0xe2, 0x37, 0x4d, 0xc1, 0xa5, 0x3e, 0x0b, 0xc8, 0x87, 0xe5, 0x92, 0x69, 0x9f, 0x02, 0xd0, 0x77, 0xd5, 0x26, 0x3c, 0xdd, 0x55, 0x60, 0x1c}),
point({0x02, 0x62, 0x2a, 0xce, 0x8f, 0x73, 0x03, 0xa3, 0x1c, 0xaf, 0xc6, 0x3f, 0x8f, 0xc4, 0x8f, 0xdc, 0x16, 0xe1, 0xc8, 0xc8, 0xd2, 0x34, 0xb2, 0xf0, 0xd6, 0x68, 0x52, 0x82, 0xa9, 0x07, 0x60, 0x31}),
point({0x20, 0x70, 0x6f, 0xd7, 0x88, 0xb2, 0x72, 0x0a, 0x1e, 0xd2, 0xa5, 0xda, 0xd4, 0x95, 0x2b, 0x01, 0xf4, 0x13, 0xbc, 0xf0, 0xe7, 0x56, 0x4d, 0xe8, 0xcd, 0xc8, 0x16, 0x68, 0x9e, 0x2d, 0xb9, 0x5f}),
point({0xbc, 0xe8, 0x3f, 0x8b, 0xa5, 0xdd, 0x2f, 0xa5, 0x72, 0x86, 0x4c, 0x24, 0xba, 0x18, 0x10, 0xf9, 0x52, 0x2b, 0xc6, 0x00, 0x4a, 0xfe, 0x95, 0x87, 0x7a, 0xc7, 0x32, 0x41, 0xca, 0xfd, 0xab, 0x42}),
point({0xe4, 0x54, 0x9e, 0xe1, 0x6b, 0x9a, 0xa0, 0x30, 0x99, 0xca, 0x20, 0x8c, 0x67, 0xad, 0xaf, 0xca, 0xfa, 0x4c, 0x3f, 0x3e, 0x4e, 0x53, 0x03, 0xde, 0x60, 0x26, 0xe3, 0xca, 0x8f, 0xf8, 0x44, 0x60}),
point({0xaa, 0x52, 0xe0, 0x00, 0xdf, 0x2e, 0x16, 0xf5, 0x5f, 0xb1, 0x03, 0x2f, 0xc3, 0x3b, 0xc4, 0x27, 0x42, 0xda, 0xd6, 0xbd, 0x5a, 0x8f, 0xc0, 0xbe, 0x01, 0x67, 0x43, 0x6c, 0x59, 0x48, 0x50, 0x1f}),
point({0x46, 0x37, 0x6b, 0x80, 0xf4, 0x09, 0xb2, 0x9d, 0xc2, 0xb5, 0xf6, 0xf0, 0xc5, 0x25, 0x91, 0x99, 0x08, 0x96, 0xe5, 0x71, 0x6f, 0x41, 0x47, 0x7c, 0xd3, 0x00, 0x85, 0xab, 0x7f, 0x10, 0x30, 0x1e}),
point({0xe0, 0xc4, 0x18, 0xf7, 0xc8, 0xd9, 0xc4, 0xcd, 0xd7, 0x39, 0x5b, 0x93, 0xea, 0x12, 0x4f, 0x3a, 0xd9, 0x90, 0x21, 0xbb, 0x68, 0x1d, 0xfc, 0x33, 0x02, 0xa9, 0xd9, 0x9a, 0x2e, 0x53, 0xe6, 0x4e})
};
static bool UnitTest(void) {
try {
ILogMessage("\tBegin ristretto255 test vectors.");
bool fFailed{ false };
int cryptoError{ 0 };
for (auto const& x : ristretto_test_vector) {
if (!x.valid()) {
cryptoError = 9;
}
}
if (cryptoError) {
errorCode = cryptoError;
szError = "Fail\tInvalid test vectors.";
ILogError(szError.c_str());
}
auto start_time{ std::chrono::high_resolution_clock::now() };
auto sclr_a = scalar::random();
auto sclr_b = scalar::random();
if (sclr_a == sclr_b)throw MyException("random not very random");
auto sclr_c = sclr_a * sclr_b;
if (sclr_b != sclr_c / sclr_a)throw MyException("multiplicative inverse not inverse");
if (false
|| !scalar::random().valid()
|| point::ptBase - point::ptBase != point::ptZero
|| scalar(1) * point::ptBase != point::ptBase
|| point::ptZero != ristretto_test_vector[0x00]
|| point::ptBase != ristretto_test_vector[0x01]
|| point::ptZero + point::ptBase != ristretto_test_vector[0x01]
|| point::ptBase + point::ptBase != ristretto_test_vector[0x02]
|| ristretto_test_vector[0x02] + point::ptBase != ristretto_test_vector[0x03]
|| ristretto_test_vector[0x02] + ristretto_test_vector[0x02] != ristretto_test_vector[0x04]
|| point::ptBase * 3 == ristretto_test_vector[0x04]
|| scalar(4).timesBase() != ristretto_test_vector[0x04]
|| ristretto_test_vector[0x03] + ristretto_test_vector[0x02] != ristretto_test_vector[0x05]
|| !(true
&& !point::ptZero
&& false == !point::ptBase
&& scalar(0).valid()==false
&& scalar(1).valid()
&& scalar::random().valid()
&& !ristretto_test_vector[0x00]
&& point::ptBase * 6 == ristretto_test_vector[0x06]
&& scalar(7) * point::ptBase == ristretto_test_vector[0x07]
&& point::ptBase * 3 + point::ptBase * 5 == ristretto_test_vector[0x08]
&& (point::ptBase * scalar(3)) * 3 == ristretto_test_vector[0x09]
&& point::ptBase * (scalar(3) * 3) == ristretto_test_vector[0x09]
&& point::ptBase * 2 + point::ptBase * 3 == ristretto_test_vector[0x0A] - ristretto_test_vector[0x05]
&& !(ristretto_test_vector[0x02] == ristretto_test_vector[0x03])
&& (point::ptBase * 2 + ristretto_test_vector[0x03]) + point::ptBase * 4 == ristretto_test_vector[0x0B] - scalar(2) * point::ptBase
&& point::ptBase * 2 + ristretto_test_vector[0x03] + scalar(4) * point::ptBase == ristretto_test_vector[0x0B] - point::ptBase * 2
&& ristretto_test_vector[0x0C] - point::ptBase * 4 == point::ptBase * 4 + ristretto_test_vector[0x02] * 2
&& ristretto_test_vector[0x0D] + scalar(-1) * ristretto_test_vector[0x0B] == point::ptBase * 2
&& ristretto_test_vector[0x0E] == scalar(2) * scalar(7).timesBase()
&& ristretto_test_vector[0x0E] == scalar(2) * scalar(7) * point::ptBase
&& ristretto_test_vector[0x0F] == scalar(6) * scalar(5) * point::ptBase - scalar(0x0F).timesBase()
&& scalar(3) == scalar(2) + scalar(1)
&& scalar(3) == scalar(4) + scalar(-1)
&& scalar(0x0103) == scalar(0xFE) + scalar(5)
&& scalar(0xDEADBEEF) == scalar(0xDEADBDDE) + scalar(0x111)
&& scalar(0xBEEFDEADBEEF) == scalar(0xBEEFDEADBDDE) + scalar(0x111)
&& scalar(0xBEEF0000000F) + scalar(-0xBEEF0EADBEEF) == scalar(int(-0xEADBEE0))
)
)
{
errorCode = 9;
szError = "Fail\tCryptographic operations fail to generate expected test vectors.";
ILogError(szError.c_str());
}
else
{
auto end_time{ std::chrono::high_resolution_clock::now() };
auto time_to_do_crypto{ std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time) };
wxLogMessage(wxT("\t\ttest of ristretto test vectors took %lld microseconds"), time_to_do_crypto.count());
ILogMessage("\tTesting generation of shared secrets.");
start_time = std::chrono::high_resolution_clock::now();
scalar sclrAnonSessionSecretKey{ scalar::random() };
point ptAnonSessionPublicKey{ sclrAnonSessionSecretKey * point::ptBase };
scalar sclrServerPemanentSecretKey{ scalar::random() };
point ptServerPermanentPublicKey{ sclrServerPemanentSecretKey * point::ptBase };
const char* MessageTextToBeSecured = "hello";
hash<512> ServerCopyOfSharedSecret((sclrServerPemanentSecretKey * ptAnonSessionPublicKey), MessageTextToBeSecured);
hash<512> AnonCopyOfSharedSecret((sclrAnonSessionSecretKey * ptServerPermanentPublicKey), MessageTextToBeSecured);
// This method identifies Server to Anon, but because Anon's key is ephemeral, does not identify Anon to Server. For mutually identified encryption, Anon has to send a signature of the emphemeral public key by a non ephemeral public key inside the encrypted message, or a signature of the cleartext inside the encrypted message by a non ephemeral public key, depending on whether he wants to prove identity at all, prove identity to only Server, or to allow Server to prove it to anyone.
if (ServerCopyOfSharedSecret != AnonCopyOfSharedSecret) {
errorCode = 10;
szError = "Fail\tShared secret was not successfully shared.";
ILogError(szError.c_str());
}
else {
end_time = std::chrono::high_resolution_clock::now();
auto time_taken{ std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time) };
wxLogMessage(_wx("\tshared secret generation both ways and comparison took %lld microseconds"), time_taken.count());
}
ILogMessage("\tTesting hashing speed");
{
int hashes{ 20000 };
point sharedSecretKey = sclrAnonSessionSecretKey * ptServerPermanentPublicKey;
ServerCopyOfSharedSecret = hash<512>(sharedSecretKey, MessageTextToBeSecured);
start_time = std::chrono::high_resolution_clock::now();
for (int i{ 0 }; i < hashes; i++) {
sharedSecretKey.blob[3] = ServerCopyOfSharedSecret.blob[5];
ServerCopyOfSharedSecret = hash<512>(sharedSecretKey, MessageTextToBeSecured);
}
end_time = std::chrono::high_resolution_clock::now();
auto time_taken{ std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time) };
{ bool f = false; //dont optimize pointless calculation
for (auto x : ServerCopyOfSharedSecret.blob) { f = f || x; }
if (f) {
wxLogMessage(_wx("\t\t%d hashes of pairs of shared secrets took %lld microseconds"), hashes, time_taken.count());
wxLogMessage(_wx("\t\tin one millisecond, can hash %lld short messages"), 1000LL*hashes / time_taken.count());
}
}
}
ILogMessage("\tTesting hashing speed, hashes of hashes");
auto first{ "first" };
auto second{ "second" };
if ( hash(first, second)
!=
hash(
hsh() << first << second
)
) throw MyException("inconsistent hashes generated on strings");
const char* _ = "hello";
hash a(_);
hash b(a);
hash c(b);
if (hash(b, c) != hash(hsh() << b << c)
) throw MyException("inconsistent hashes generated");
constexpr int hashes{ 3000 };
start_time = std::chrono::high_resolution_clock::now();
for (int i{ 0 }; i < hashes; i++) {
a = hash(b, c);
b = hash(c, a);
c = hash(a, b);
}
end_time = std::chrono::high_resolution_clock::now();
auto time_taken{ std::chrono::duration_cast<std::chrono::microseconds> (end_time - start_time) };
{ bool f = false; //dont optimize pointless calculation away
for (auto x : c.blob) { f = f || x; }
if (f) {
wxLogMessage(_wx("\t\t%d hashes of a pair of hashes took %lld microseconds"), hashes * 3, time_taken.count());
if (time_taken.count() > 0) {
wxLogMessage(_wx("\t\tin one millisecond, can hash %lld pairs of 256 bit hashes"), hashes * 3000ll / time_taken.count());
}
}
}
uint8_t buff[hashes * 3 * 64];
randombytes_buf(std::span<byte>(buff));
start_time = std::chrono::high_resolution_clock::now();
c = hash(buff);
end_time = std::chrono::high_resolution_clock::now();
time_taken = std::chrono::duration_cast<std::chrono::microseconds> (end_time - start_time);
{ bool f = false; //dont optimize pointless calculation away
for (auto x : c.blob) { f = f || x; }
if (f) {
wxLogMessage(_wx("\t\tone hash of %d bytes took %lld microseconds"), hashes * 3 * 64, time_taken.count());
}
}
}
}
catch (const std::exception & e) {
errorCode = 10;
szError = e.what();
ILogError(szError.c_str());
}
catch (...) {
szError = sz_unknown_error;
errorCode = 10;
ILogError(szError.c_str());
}
unit_test_action = &TestSignatures;
return true;
}
} //end ristretto255 namespace
static void bigEndian(std::array<uint8_t, 8> & out, uint64_t in) {
for (int i = 7; i >= 0; i-- ) {
out[i] = in & 0xFF;
in = in >> 8;
}
}
static uint64_t shiftArbRight(uint64_t in, unsigned int i) {
if (i > 0) {
//shifting right any number of bits
if (i > 63) in = 0;
else in = (in >> i);
}
return in;
}
static uint64_t shiftArbLeft(uint64_t in, unsigned int i) {
//shifting left any number of bits
if (i > 0) {
if (i > 63)in = 0;
else in = (in << i);
}
return in;
}
//extern std::array<const uint8_t, 0x100>reverseLookup();
static bool slash6UnitTest(void) {
try {
static int count{ 0 };
if (count == 0) {
wxLogMessage(_wx(R"( decoding slash6 test vector to get expected result vector)"));
{
std::array<uint8_t, 56> a;
a[51] = a[52] = a[53] = a[54] = a[55] = 0xcc;
a[51] = 0xFF;
decltype(a) expectedResult{ 0x00, 0x00, 0x01, 0x04, 0x10, 0x83, 0x10, 0x51, 0x87, 0x20, 0x92, 0x8b, 0x30, 0xd3, 0x8f, 0x41, 0x14, 0x93, 0x51, 0x55, 0x97, 0x61, 0x96, 0x9b, 0x71, 0xd7, 0x9f, 0x82, 0x18, 0xa3, 0x92, 0x59, 0xa7, 0xa2, 0x9a, 0xab, 0xb2, 0xdb, 0xaf, 0xc3, 0x1c, 0xb3, 0xd3, 0x5d, 0xb7, 0xe3, 0x9e, 0xbb, 0xf3, 0xdf, 0xbf,0xff,0xcc,0xcc,0xcc,0xcc };
// Trying it with guard bytes at the end.
if (base64_to_bits(&a[0], 0, 408, "0oOlI123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz!$*+-_") != 408
|| a != expectedResult) {
errorCode = 11;
szError = "Fail\tunexpected result of test vector.";
ILogError(szError.c_str());
}
}
{
std::array<uint8_t, 51> a;
decltype(a) expectedResult{ 0x00, 0x00, 0x01, 0x04, 0x10, 0x83, 0x10, 0x51, 0x87, 0x20, 0x92, 0x8b, 0x30, 0xd3, 0x8f, 0x41, 0x14, 0x93, 0x51, 0x55, 0x97, 0x61, 0x96, 0x9b, 0x71, 0xd7, 0x9f, 0x82, 0x18, 0xa3, 0x92, 0x59, 0xa7, 0xa2, 0x9a, 0xab, 0xb2, 0xdb, 0xaf, 0xc3, 0x1c, 0xb3, 0xd3, 0x5d, 0xb7, 0xe3, 0x9e, 0xbb, 0xf3, 0xdf, 0xbf };
// Trying it without guard bytes to provoke address sanitizer.
if (base64_to_bits(&a[0], 0, 408, "0oOlI123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz!$*+-_") != 408
|| a != expectedResult) {
errorCode = 11;
szError = "Fail\tunexpected result of test vector.";
ILogError(szError.c_str());
}
}
{
std::array<uint8_t, 10> buff;
randombytes_buf(buff);
std::array<char, 13>txt;
bits2base64(&buff[0], 0, 72, txt);
wxLogMessage(_wx("\trandom 72 bit integer: %s"), &txt[0]);
wxLogMessage(_wx("\tRandom text secret %s"), &DeriveTextSecret(ristretto255::scalar::random(), 1)[0]);
}
}
std::array<uint8_t, 8> buff;
randombytes_buf(buff);
std::array<char, 9 * 8 / 6 + 1>txt;
decltype(buff) buff2;
randombytes_buf(buff2);
randombytes_buf(txt);
decltype(buff2) buff3{ buff2 };
unsigned int length{ buff2[1] };
length = length % 60;
unsigned int start{ buff2[2] };
start = start % (64 - length);
unsigned int startDest{ buff2[3] };
startDest = startDest % (64 - length);
uint64_t sourceAs64bitInt{ 0 };
for (auto x : buff) sourceAs64bitInt = (sourceAs64bitInt << 8) | x;
if (count == 0)wxLogMessage(_wx("\tGenerating slash6 representation of %u random bits and converting back:"), length);
bits2base64(&buff[0], start, length, txt);
uint64_t originalStateOfDestination{ 0 };
for (auto x : buff2) originalStateOfDestination = (originalStateOfDestination << 8) | x;
/*wxLogDebug("start=%u, length=%u", start, length);
wxLogDebug("%02x%02x%02x%02x%02x%02x%02x%02x", buff[0], buff[1], buff[2], buff[3], buff[4], buff[5], buff[6], buff[7]);
wxLogDebug("%02x%02x%02x%02x%02x%02x%02x%02x", buff2[0], buff2[1], buff2[2], buff2[3], buff2[4], buff2[5], buff2[6], buff2[7]);*/
unsigned int newLength{ base64_to_bits(&buff2[0], startDest, length, &txt[0]) };
if (newLength != length) {
errorCode = 11;
szError = "Fail\tBitBuffer length discrepancy.";
ILogError(szError.c_str());
}
/*wxLogDebug("startDest=%u", startDest);
wxLogDebug("%02x%02x%02x%02x%02x%02x%02x%02x", buff2[0], buff2[1], buff2[2], buff2[3], buff2[4], buff2[5], buff2[6], buff2[7]);*/
uint64_t finalStateOfDestination{ 0 };
for (auto x : buff2) finalStateOfDestination = (finalStateOfDestination << 8) | x;
uint64_t src{ shiftArbRight(shiftArbLeft(sourceAs64bitInt, start),(64 - length)) };
uint64_t dest{ shiftArbRight(shiftArbLeft(finalStateOfDestination, startDest),(64 - length)) };
uint64_t destOriginalLeadingBits{ shiftArbRight(originalStateOfDestination, 64 - startDest) };
uint64_t destFinalLeadingBits{ shiftArbRight(finalStateOfDestination, 64 - startDest) };
uint64_t destOriginalTrailingBits{ shiftArbLeft(originalStateOfDestination, (startDest + length)) };
uint64_t destFinalTrailingBits{ shiftArbLeft(finalStateOfDestination, (startDest + length)) };
/*wxLogDebug("check bits %d", int(src));*/
if (src != dest
|| destOriginalLeadingBits != destFinalLeadingBits
|| destOriginalTrailingBits != destFinalTrailingBits) {
wxLogMessage(_wx("start=%u, Destination start =%u, length=%u"), start, startDest, length);
wxLogMessage(_wx("%02x%02x%02x%02x%02x%02x%02x%02x"), buff[0], buff[1], buff[2], buff[3], buff[4], buff[5], buff[6], buff[7]);
wxLogMessage(_wx("%02x%02x%02x%02x%02x%02x%02x%02x"), buff3[0], buff3[1], buff2[3], buff3[3], buff3[4], buff3[5], buff3[6], buff3[7]);
wxLogMessage(_wx("%02x%02x%02x%02x%02x%02x%02x%02x"), buff2[0], buff2[1], buff2[2], buff2[3], buff2[4], buff2[5], buff2[6], buff2[7]);
if (src != dest) {
wxLogMessage(_wx("Bits changed in move, %x, %x"), (unsigned int)src, (unsigned int)dest);
}
if (destOriginalLeadingBits != destFinalLeadingBits) {
wxLogMessage(_wx("Bits preceding bit buffer changed, %x, %x"), (unsigned int)destOriginalLeadingBits, (unsigned int)destFinalLeadingBits);
}
if (destOriginalTrailingBits != destFinalTrailingBits) {
wxLogMessage(_wx("Bits following bit buffer changed, %x, %x"), (unsigned int)destOriginalTrailingBits, (unsigned int)destFinalTrailingBits);
}
errorCode = 12;
szError = "Fail\tBitBuffer discrepancy.";
ILogError(szError.c_str());
}
count++;
if (count > 4) {
wxLogMessage(_wx("\tSlash6 random bits test repeated %d times with different data, sizes, and positions"), count);
unit_test_action = &ristretto255::UnitTest;
}
// If you touch the slash6 encoding, try retesting with the number of tests tries set to one hundred thousand instead of five, to catch all code paths and weird special cases.
}
catch (const std::exception & e) {
errorCode = 12;
szError = e.what();
ILogError(szError.c_str());
}
catch (...) {
szError = sz_unknown_error;
errorCode = 12;
ILogError(szError.c_str());
}
return true;
}
static bool StartUnitTest(void){
modal_dialog_hook.Register();
ILogMessage("Begin Unit Test for code compiled " __DATE__ " " __TIME__ ".");
// singletonFrame->m_pLogWindow->GetFrame()->Raise();
unit_test_action = &slash6UnitTest;
return true;
}
void UnitTest(wxIdleEvent & event) {
if((* unit_test_action)())event.RequestMore(true);
event.Skip();
}