From f05f533a0c27a9a255dbea78265266191c248800 Mon Sep 17 00:00:00 2001 From: sowle Date: Wed, 23 Dec 2020 01:40:02 +0300 Subject: [PATCH] experimental crypto: tests moved to functional_tests, few tests added, gtest-like infrastructure added --- .../crypto_tests.cpp | 415 +++++++++++------- tests/functional_tests/crypto_tests.h | 6 + tests/functional_tests/main.cpp | 9 +- 3 files changed, 262 insertions(+), 168 deletions(-) rename tests/{unit_tests => functional_tests}/crypto_tests.cpp (67%) create mode 100644 tests/functional_tests/crypto_tests.h diff --git a/tests/unit_tests/crypto_tests.cpp b/tests/functional_tests/crypto_tests.cpp similarity index 67% rename from tests/unit_tests/crypto_tests.cpp rename to tests/functional_tests/crypto_tests.cpp index 366a1145..ae79d65c 100644 --- a/tests/unit_tests/crypto_tests.cpp +++ b/tests/functional_tests/crypto_tests.cpp @@ -3,79 +3,18 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #define USE_INSECURE_RANDOM_RPNG_ROUTINES // turns on random manupulation for tests -#include "gtest/gtest.h" +#include #include "crypto/crypto.h" +#include "epee/include/misc_log_ex.h" +#include "epee/include/profile_tools.h" +#include "include_base_utils.h" +#include "common/crypto_stream_operators.h" + extern "C" { #include "crypto/crypto-ops.h" -void fe_reduce(fe u, const fe h) -{ - /* From fe_frombytes.c */ - - // loading changed - - int64_t h0 = h[0]; - int64_t h1 = h[1]; - int64_t h2 = h[2]; - int64_t h3 = h[3]; - int64_t h4 = h[4]; - int64_t h5 = h[5]; - int64_t h6 = h[6]; - int64_t h7 = h[7]; - int64_t h8 = h[8]; - int64_t h9 = h[9]; - int64_t carry0; - int64_t carry1; - int64_t carry2; - int64_t carry3; - int64_t carry4; - int64_t carry5; - int64_t carry6; - int64_t carry7; - int64_t carry8; - int64_t carry9; - - carry9 = (h9 + (int64_t)(1 << 24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; - carry1 = (h1 + (int64_t)(1 << 24)) >> 25; h2 += carry1; h1 -= carry1 << 25; - carry3 = (h3 + (int64_t)(1 << 24)) >> 25; h4 += carry3; h3 -= carry3 << 25; - carry5 = (h5 + (int64_t)(1 << 24)) >> 25; h6 += carry5; h5 -= carry5 << 25; - carry7 = (h7 + (int64_t)(1 << 24)) >> 25; h8 += carry7; h7 -= carry7 << 25; - - carry0 = (h0 + (int64_t)(1 << 25)) >> 26; h1 += carry0; h0 -= carry0 << 26; - carry2 = (h2 + (int64_t)(1 << 25)) >> 26; h3 += carry2; h2 -= carry2 << 26; - carry4 = (h4 + (int64_t)(1 << 25)) >> 26; h5 += carry4; h4 -= carry4 << 26; - carry6 = (h6 + (int64_t)(1 << 25)) >> 26; h7 += carry6; h6 -= carry6 << 26; - carry8 = (h8 + (int64_t)(1 << 25)) >> 26; h9 += carry8; h8 -= carry8 << 26; - - u[0] = h0; - u[1] = h1; - u[2] = h2; - u[3] = h3; - u[4] = h4; - u[5] = h5; - u[6] = h6; - u[7] = h7; - u[8] = h8; - u[9] = h9; - - /* End fe_frombytes.c */ - -} - - -void sc_mul(unsigned char *s, const unsigned char *a, const unsigned char *b) -{ - unsigned char c[32]; - unsigned char neg_a[32]; - sc_0(c); - sc_sub(neg_a, c, a); - // s = c - ab - sc_mulsub(s, neg_a, b, c); -} - } // extern "C" - unsigned char Lm2[32] = { 0xeb, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10 }; // out = z ^ s (mod l) @@ -105,25 +44,24 @@ void sc_exp(unsigned char* out, const unsigned char* z, const unsigned char* s) } } - //memcpy(out, z, sizeof(crypto::ec_scalar)); - for (size_t i = msb_s; i != SIZE_MAX; --i) { sc_mul(out, out, out); - std::cout << "sc_mul(out, out, out);" << std::endl; + //std::cout << "sc_mul(out, out, out);" << std::endl; uint8_t bit = (s[i / 8] >> (i % 8)) & 1; if (bit) { sc_mul(out, out, z); - std::cout << "sc_mul(out, out, z);" << std::endl; + //std::cout << "sc_mul(out, out, z);" << std::endl; } } } +// out = z ^ -1 (= z ^ (L - 2) according to Fermat little theorem) void sc_invert(unsigned char* out, const unsigned char* z) { memcpy(out, z, sizeof(crypto::ec_scalar)); - for(size_t i = 0; i < 128; ++i) + for (size_t i = 0; i < 128; ++i) sc_mul(out, out, out); sc_mul(out, out, z); sc_mul(out, out, out); @@ -324,7 +262,9 @@ void sc_invert(unsigned char* out, const unsigned char* z) } - +// +// Helpers +// template std::string pod_to_hex_big_endian(const pod_t &h) @@ -335,13 +275,21 @@ std::string pod_to_hex_big_endian(const pod_t &h) std::string s(len * 2, ' '); for (size_t i = 0; i < len; ++i) { - s[2 * i] = hexmap[(data[len - 1 - i] & 0xF0) >> 4]; + s[2 * i] = hexmap[(data[len - 1 - i] & 0xF0) >> 4]; s[2 * i + 1] = hexmap[(data[len - 1 - i] & 0x0F)]; } return s; } +uint64_t rand_in_range(uint64_t from_including, uint64_t to_not_including) +{ + uint64_t result = 0; + crypto::generate_random_bytes(sizeof result, &result); + return from_including + result % (to_not_including - from_including); +} + + int fe_cmp(const fe a, const fe b) { @@ -462,55 +410,8 @@ struct scalar_t scalar_t reciprocal() const { - /*unsigned char bytes[32] = {2}; - fe t; - fe_frombytes(t, (unsigned char*)&bytes); - fe r; - fe_invert(r, t); - fe m; - fe_mul(m, r, t); - - - fe r2; - my_fe_invert(r2, t); - fe m2; - fe_mul(m2, r2, t); - scalar_t result; - fe v_f; - fe result_f; - fe_frombytes(v_f, reinterpret_cast(&m_s)); - my_fe_invert(result_f, v_f); - fe_tobytes(reinterpret_cast(&result), result_f); - //sc_reduce(reinterpret_cast(&result)); - fe result_check; - fe_frombytes(result_check, reinterpret_cast(&result)); - fe v_check; - fe_invert(v_check, result_check); - //sc_reduce(reinterpret_cast(&result)); - return result;*/ - - scalar_t result; - fe v_f; - fe result_f; - fe_frombytes(v_f, reinterpret_cast(&m_s)); - fe_invert(result_f, v_f); - - /*fe x2; - fe_mul(x2, result_f, v_f); - - fe_reduce(result_f, result_f); - if (fe_cmp(result_f, scalar_L_fe) > 0) - { - // result_f > L - fe_sub(result_f, result_f, scalar_L_fe); - if (fe_cmp(result_f, scalar_L_fe) >= 0) - return false; // fail - }*/ - unsigned char tmp[64] = { 0 }; - fe_tobytes(reinterpret_cast(&tmp), result_f); - sc_reduce(tmp); - memcpy(&result, &tmp, sizeof result); + sc_invert(&result.m_s[0], &m_s[0]); return result; } @@ -558,10 +459,10 @@ struct scalar_t { return ss << "0x" << pod_to_hex_big_endian(v); } - + }; // struct scalar_t -//__declspec(align(32)) + //__declspec(align(32)) struct point_t { // A point(x, y) is represented in extended homogeneous coordinates (X, Y, Z, T) @@ -618,7 +519,7 @@ struct point_t return result; } - friend bool operator==(const point_t& lhs, const point_t& rhs) + friend bool operator==(const point_t& lhs, const point_t& rhs) { // convert to xy form, then compare components (because (z, y, z, t) representation is not unique) fe lrecip, lx, ly; @@ -657,7 +558,7 @@ struct point_g_t : public point_t /*friend point_t operator*(const int64_t lhs, const point_g_t& rhs) { - return operator*(scalar_t) + return operator*(scalar_t) }*/ static_assert(sizeof(crypto::public_key) == 32, "size error"); @@ -666,14 +567,176 @@ struct point_g_t : public point_t static const point_g_t point_G; -static const scalar_t scalar_L = { 0x5812631a5cf5d3ed, 0x14def9dea2f79cd6, 0x0, 0x1000000000000000 }; -static const scalar_t scalar_Lm1 = { 0x5812631a5cf5d3ec, 0x14def9dea2f79cd6, 0x0, 0x1000000000000000 }; -static const scalar_t scalar_P = { 0xffffffffffffffed, 0xffffffffffffffff, 0xffffffffffffffff, 0x7fffffffffffffff }; -static const scalar_t scalar_Pm1 = { 0xffffffffffffffec, 0xffffffffffffffff, 0xffffffffffffffff, 0x7fffffffffffffff }; -static const scalar_t scalar_256m1 = { 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff }; +static const scalar_t scalar_L = { 0x5812631a5cf5d3ed, 0x14def9dea2f79cd6, 0x0, 0x1000000000000000 }; +static const scalar_t scalar_Lm1 = { 0x5812631a5cf5d3ec, 0x14def9dea2f79cd6, 0x0, 0x1000000000000000 }; +static const scalar_t scalar_P = { 0xffffffffffffffed, 0xffffffffffffffff, 0xffffffffffffffff, 0x7fffffffffffffff }; +static const scalar_t scalar_Pm1 = { 0xffffffffffffffec, 0xffffffffffffffff, 0xffffffffffffffff, 0x7fffffffffffffff }; +static const scalar_t scalar_256m1 = { 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff }; + + +/* +* tiny facade to gtest-alike interface to make simplier further tests transfer to unit_tests +*/ +#define TEST(test_name_a, test_name_b) \ + static bool test_name_a ## _ ## test_name_b(); \ + static test_keeper_t test_name_a ## _ ## test_name_b ## keeper(STR(COMBINE(test_name_a ## _, test_name_b)), & test_name_a ## _ ## test_name_b); \ + static bool test_name_a ## _ ## test_name_b() +#define ASSERT_TRUE(expr) CHECK_AND_ASSERT_MES(expr, false, "This is not true: " #expr) +#define ASSERT_FALSE(expr) CHECK_AND_ASSERT_MES((expr) == false, false, "This is not false: " #expr) +#define ASSERT_EQ(a, b) CHECK_AND_ASSERT_MES(a == b, false, #a " != " #b) + +typedef bool(*bool_func_ptr_t)(); +static std::vector> g_tests; +struct test_keeper_t +{ + test_keeper_t(const char* name, bool_func_ptr_t func_p) + { + g_tests.push_back(std::make_pair(name, func_p)); + } +}; + + +// +// Tests +// + +struct sig_check_t +{ + crypto::hash prefix_hash; + crypto::key_image ki; + std::vector pub_keys; + std::vector pub_keys_p; + crypto::secret_key xi; + size_t secret_key_index; + std::vector sigs; + + sig_check_t() + {} + + void prepare_random_data(size_t decoy_set_size) + { + crypto::public_key Pi; + crypto::generate_keys(Pi, xi); + + crypto::generate_random_bytes(sizeof prefix_hash, &prefix_hash); + + for (size_t i = 0; i < decoy_set_size; ++i) + { + crypto::public_key p; + crypto::secret_key s; + crypto::generate_keys(p, s); + pub_keys.push_back(p); + } + + secret_key_index = rand_in_range(0, pub_keys.size()); + pub_keys.insert(pub_keys.begin() + secret_key_index, Pi); + + for (auto& pk : pub_keys) + pub_keys_p.push_back(&pk); + + crypto::generate_key_image(Pi, xi, ki); + + sigs.resize(pub_keys.size()); + } + + void generate() + { + crypto::generate_ring_signature(prefix_hash, ki, pub_keys_p, xi, secret_key_index, sigs.data()); + } + + bool check() + { + return crypto::check_ring_signature(prefix_hash, ki, pub_keys_p, sigs.data()); + } +}; + + +TEST(crypto, ring_sigs) +{ + size_t n = 1000; + size_t decoy_set = 2; + + std::vector sigs; + sigs.resize(n); + + for (size_t i = 0; i < sigs.size(); ++i) + sigs[i].prepare_random_data(decoy_set); + + std::cout << n << " random sigs prepared" << std::endl; + + bool r = true; + + TIME_MEASURE_START(gen_mcs); + for (size_t i = 0; i < sigs.size(); ++i) + { + sigs[i].generate(); + } + TIME_MEASURE_FINISH(gen_mcs); + + std::cout << n << " random sigs generated in " << gen_mcs / 1000 << " s" << std::endl; + + TIME_MEASURE_START(check_mcs); + for (size_t i = 0; i < sigs.size(); ++i) + { + if (!sigs[i].check()) + { + r = false; + break; + } + } + TIME_MEASURE_FINISH(check_mcs); + + ASSERT_TRUE(r); + + std::cout << n << " random sigs checked:" << std::endl; + std::cout << " " << std::right << std::setw(8) << gen_mcs / 1000 << " ms for generation total" << std::endl; + std::cout << " " << std::right << std::setw(8) << std::fixed << std::setprecision(1) << double(gen_mcs) / n << " mcs for generating per one signature" << std::endl; + std::cout << " " << std::right << std::setw(8) << check_mcs / 1000 << " ms for checking total" << std::endl; + std::cout << " " << std::right << std::setw(8) << std::fixed << std::setprecision(1) << double(check_mcs) / n << " mcs for checking per one signature" << std::endl; + + return true; +} + +TEST(crypto, keys) +{ + // keypair: sk: 407b3b73df8f11737494bdde6ca47a42e1b537390aec2fa781a2d170335c440f + // pk: 1b546af91d31fdb1c476fd62fbb65b6fd5ed47804185fc77d48bc4cc00f47ef0 + + bool r = false; + crypto::public_key pk; + r = epee::string_tools::parse_tpod_from_hex_string("1b546af91d31fdb1c476fd62fbb65b6fd5ed47804185fc77d48bc4cc00f47ef0", pk); + ASSERT_TRUE(r); + + crypto::secret_key sk; + r = epee::string_tools::parse_tpod_from_hex_string("407b3b73df8f11737494bdde6ca47a42e1b537390aec2fa781a2d170335c440f", sk); + ASSERT_TRUE(r); + + crypto::public_key pk2; + r = crypto::secret_key_to_public_key(sk, pk2); + ASSERT_TRUE(r); + + std::cout << pk << std::endl; + std::cout << pk2 << std::endl; + + ASSERT_EQ(pk, pk2); + ASSERT_TRUE(crypto::check_key(pk)); + ASSERT_TRUE(crypto::check_key(pk2)); + + std::cout << std::endl; + + crypto::generate_keys(pk, sk); + r = crypto::secret_key_to_public_key(sk, pk2); + ASSERT_TRUE(r); + + ASSERT_EQ(pk, pk2); + + ASSERT_TRUE(crypto::check_key(pk)); + ASSERT_TRUE(crypto::check_key(pk2)); + + return true; +} -/* temporary disable in order not to break the compilation TEST(crypto, scalar_basics) { scalar_t zero = 0; @@ -686,8 +749,8 @@ TEST(crypto, scalar_basics) { z.make_random(); ASSERT_FALSE(z.is_zero()); - ASSERT_GT(z, z - 1); - ASSERT_LT(z, z + 1); + ASSERT_TRUE(z > z - 1); + ASSERT_TRUE(z < z + 1); } ASSERT_TRUE(scalar_L > 0 && !(scalar_L < 0)); @@ -715,37 +778,6 @@ TEST(crypto, scalar_basics) - fe L_fe; - fe_frombytes(L_fe, &scalar_L.m_s[0]); - - fe Pm1_fe; - fe_frombytes(Pm1_fe, &scalar_Pm1.m_s[0]); - - fe r; - fe f_1 = { 1 }; - fe_add(r, Pm1_fe, f_1); - - - - while(true) - { - static int ti = 2; - static int pi = 3; - scalar_t t = ti; - scalar_t p = pi; - scalar_t r = 0; - //sc_exp(r.data(), t.data(), Lm2); - - sc_invert(r.data(), t.data()); - - std::cout << ti << " ^ L-2" << " = " << r << std::endl; - - r = r * 6; - std::cout << r << std::endl; - } - - - scalar_t a = 2; a = a / 2; std::cout << "2 / 2 = " << a << std::endl; @@ -754,6 +786,7 @@ TEST(crypto, scalar_basics) a = a * 2; std::cout << "L-1 / 2 * 2 = " << a << std::endl; + return true; } TEST(crypto, point_basics) @@ -768,11 +801,13 @@ TEST(crypto, point_basics) ASSERT_TRUE(C - K == E); ASSERT_TRUE(C - E == K); ASSERT_TRUE(C == 193851 * point_G); + + return true; } TEST(crypto, scalar_reciprocal) { - int64_t test_nums[] = {1, 2, 10}; + int64_t test_nums[] = { 1, 2, 10 }; for (size_t i = 0; i < sizeof test_nums / sizeof test_nums[0]; ++i) { @@ -787,6 +822,8 @@ TEST(crypto, scalar_reciprocal) scalar_t m = e * d; ASSERT_TRUE(m == s); + + return true; } @@ -798,6 +835,50 @@ TEST(crypto, scalars) scalar_t m = e * d; ASSERT_TRUE(m == s); + + return true; } -*/ \ No newline at end of file + +int crypto_tests() +{ + epee::log_space::get_set_log_detalisation_level(true, LOG_LEVEL_1); + epee::log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL, LOG_LEVEL_2); + epee::log_space::log_singletone::add_logger(LOGGER_FILE, + epee::log_space::log_singletone::get_default_log_file().c_str(), + epee::log_space::log_singletone::get_default_log_folder().c_str()); + + + + std::vector failed_tests; + for (size_t i = 0; i < g_tests.size(); ++i) + { + auto& test = g_tests[i]; + bool r = test.second(); + if (r) + { + LOG_PRINT_GREEN(" " << std::setw(40) << std::left << test.first << "OK", LOG_LEVEL_0); + } + else + { + LOG_PRINT_RED(ENDL << " " << std::setw(40) << std::left << test.first << "FAILED" << ENDL, LOG_LEVEL_0); + failed_tests.push_back(i); + } + } + + if (failed_tests.empty()) + { + LOG_PRINT_GREEN(ENDL, LOG_LEVEL_0); + LOG_PRINT_GREEN("All tests passed okay", LOG_LEVEL_0); + return 0; + } + + LOG_PRINT_RED_L0(ENDL, LOG_LEVEL_0); + LOG_PRINT_RED_L0(ENDL << "Failed tests:"); + for (size_t i : failed_tests) + { + LOG_PRINT_RED_L0(g_tests[i].first); + } + + return 1; +} diff --git a/tests/functional_tests/crypto_tests.h b/tests/functional_tests/crypto_tests.h new file mode 100644 index 00000000..492b27e0 --- /dev/null +++ b/tests/functional_tests/crypto_tests.h @@ -0,0 +1,6 @@ +// Copyright (c) 2020 Zano Project +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#pragma once + +int crypto_tests(); diff --git a/tests/functional_tests/main.cpp b/tests/functional_tests/main.cpp index 00f038d3..65acf07d 100644 --- a/tests/functional_tests/main.cpp +++ b/tests/functional_tests/main.cpp @@ -19,6 +19,7 @@ using namespace epee; #include "deadlock_guard_test.h" #include "difficulty_analysis.h" #include "plain_wallet_tests.h" +#include "crypto_tests.h" namespace po = boost::program_options; @@ -55,6 +56,7 @@ namespace const command_line::arg_descriptor arg_deadlock_guard = { "test-deadlock-guard", "Do deadlock guard test", false, true }; const command_line::arg_descriptor arg_difficulty_analysis = { "difficulty-analysis", "Do difficulty analysis", "", true }; const command_line::arg_descriptor arg_test_plain_wallet = { "test-plainwallet", "Do testing of plain wallet interface", false, true }; + const command_line::arg_descriptor arg_crypto_tests = { "crypto-tests", "Run experimental crypto tests", false, true }; } @@ -106,7 +108,8 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_options, arg_max_tx_in_pool); command_line::add_arg(desc_options, arg_deadlock_guard); command_line::add_arg(desc_options, arg_difficulty_analysis); - command_line::add_arg(desc_options, arg_test_plain_wallet); + command_line::add_arg(desc_options, arg_test_plain_wallet); + command_line::add_arg(desc_options, arg_crypto_tests); test_serialization(); @@ -209,6 +212,10 @@ int main(int argc, char* argv[]) } return 0; } + else if (command_line::get_arg(vm, arg_crypto_tests)) + { + return crypto_tests(); + } else { std::cout << desc_options << std::endl;