diff options
Diffstat (limited to 'tests/unit_tests')
-rw-r--r-- | tests/unit_tests/CMakeLists.txt | 7 | ||||
-rw-r--r-- | tests/unit_tests/account.cpp | 71 | ||||
-rw-r--r-- | tests/unit_tests/crypto.cpp | 23 | ||||
-rw-r--r-- | tests/unit_tests/hardfork.cpp | 1 | ||||
-rw-r--r-- | tests/unit_tests/json_serialization.cpp | 217 | ||||
-rw-r--r-- | tests/unit_tests/mlocker.cpp | 186 | ||||
-rw-r--r-- | tests/unit_tests/mnemonics.cpp | 30 | ||||
-rw-r--r-- | tests/unit_tests/multisig.cpp | 15 | ||||
-rw-r--r-- | tests/unit_tests/ringdb.cpp | 22 | ||||
-rw-r--r-- | tests/unit_tests/serialization.cpp | 5 | ||||
-rw-r--r-- | tests/unit_tests/wipeable_string.cpp | 204 |
11 files changed, 761 insertions, 20 deletions
diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index 3c7414640..145e3820e 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -27,6 +27,7 @@ # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. set(unit_tests_sources + account.cpp apply_permutation.cpp address_from_url.cpp ban.cpp @@ -47,11 +48,13 @@ set(unit_tests_sources epee_levin_protocol_handler_async.cpp epee_utils.cpp fee.cpp + json_serialization.cpp get_xtype_from_string.cpp hashchain.cpp http.cpp main.cpp memwipe.cpp + mlocker.cpp mnemonics.cpp mul_div.cpp multisig.cpp @@ -72,7 +75,8 @@ set(unit_tests_sources ringct.cpp output_selection.cpp vercmp.cpp - ringdb.cpp) + ringdb.cpp + wipeable_string.cpp) set(unit_tests_headers unit_tests_utils.h) @@ -87,6 +91,7 @@ target_link_libraries(unit_tests cryptonote_core blockchain_db rpc + serialization wallet p2p version diff --git a/tests/unit_tests/account.cpp b/tests/unit_tests/account.cpp new file mode 100644 index 000000000..113622b5e --- /dev/null +++ b/tests/unit_tests/account.cpp @@ -0,0 +1,71 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "gtest/gtest.h" + +#include "cryptonote_basic/account.h" + +TEST(account, encrypt_keys) +{ + cryptonote::keypair recovery_key = cryptonote::keypair::generate(hw::get_device("default")); + cryptonote::account_base account; + crypto::secret_key key = account.generate(recovery_key.sec); + const cryptonote::account_keys keys = account.get_keys(); + + ASSERT_EQ(account.get_keys().m_account_address, keys.m_account_address); + ASSERT_EQ(account.get_keys().m_spend_secret_key, keys.m_spend_secret_key); + ASSERT_EQ(account.get_keys().m_view_secret_key, keys.m_view_secret_key); + ASSERT_EQ(account.get_keys().m_multisig_keys, keys.m_multisig_keys); + + crypto::chacha_key chacha_key; + crypto::generate_chacha_key(&recovery_key, sizeof(recovery_key), chacha_key, 1); + + account.encrypt_keys(chacha_key); + + ASSERT_EQ(account.get_keys().m_account_address, keys.m_account_address); + ASSERT_NE(account.get_keys().m_spend_secret_key, keys.m_spend_secret_key); + ASSERT_NE(account.get_keys().m_view_secret_key, keys.m_view_secret_key); + + account.decrypt_viewkey(chacha_key); + + ASSERT_EQ(account.get_keys().m_account_address, keys.m_account_address); + ASSERT_NE(account.get_keys().m_spend_secret_key, keys.m_spend_secret_key); + ASSERT_EQ(account.get_keys().m_view_secret_key, keys.m_view_secret_key); + + account.encrypt_viewkey(chacha_key); + + ASSERT_EQ(account.get_keys().m_account_address, keys.m_account_address); + ASSERT_NE(account.get_keys().m_spend_secret_key, keys.m_spend_secret_key); + ASSERT_NE(account.get_keys().m_view_secret_key, keys.m_view_secret_key); + + account.decrypt_keys(chacha_key); + + ASSERT_EQ(account.get_keys().m_account_address, keys.m_account_address); + ASSERT_EQ(account.get_keys().m_spend_secret_key, keys.m_spend_secret_key); + ASSERT_EQ(account.get_keys().m_view_secret_key, keys.m_view_secret_key); +} diff --git a/tests/unit_tests/crypto.cpp b/tests/unit_tests/crypto.cpp index 4bed06173..29fa88f9d 100644 --- a/tests/unit_tests/crypto.cpp +++ b/tests/unit_tests/crypto.cpp @@ -73,3 +73,26 @@ TEST(Crypto, Ostream) EXPECT_TRUE(is_formatted<crypto::key_derivation>()); EXPECT_TRUE(is_formatted<crypto::key_image>()); } + +TEST(Crypto, null_keys) +{ + char zero[32]; + memset(zero, 0, 32); + ASSERT_EQ(memcmp(crypto::null_skey.data, zero, 32), 0); + ASSERT_EQ(memcmp(crypto::null_pkey.data, zero, 32), 0); +} + +TEST(Crypto, verify_32) +{ + // all bytes are treated the same, so we can brute force just one byte + unsigned char k0[32] = {0}, k1[32] = {0}; + for (unsigned int i0 = 0; i0 < 256; ++i0) + { + k0[0] = i0; + for (unsigned int i1 = 0; i1 < 256; ++i1) + { + k1[0] = i1; + ASSERT_EQ(!crypto_verify_32(k0, k1), i0 == i1); + } + } +} diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp index 930aeb782..8155c65f9 100644 --- a/tests/unit_tests/hardfork.cpp +++ b/tests/unit_tests/hardfork.cpp @@ -93,7 +93,6 @@ public: virtual uint64_t get_num_outputs(const uint64_t& amount) const { return 1; } virtual uint64_t get_indexing_base() const { return 0; } virtual output_data_t get_output_key(const uint64_t& amount, const uint64_t& index) { return output_data_t(); } - virtual output_data_t get_output_key(const uint64_t& global_index) const { return output_data_t(); } virtual tx_out_index get_output_tx_and_index_from_global(const uint64_t& index) const { return tx_out_index(); } virtual tx_out_index get_output_tx_and_index(const uint64_t& amount, const uint64_t& index) const { return tx_out_index(); } virtual void get_output_tx_and_index(const uint64_t& amount, const std::vector<uint64_t> &offsets, std::vector<tx_out_index> &indices) const {} diff --git a/tests/unit_tests/json_serialization.cpp b/tests/unit_tests/json_serialization.cpp new file mode 100644 index 000000000..ac6b60846 --- /dev/null +++ b/tests/unit_tests/json_serialization.cpp @@ -0,0 +1,217 @@ + +#include <boost/optional/optional.hpp> +#include <boost/range/adaptor/indexed.hpp> +#include <gtest/gtest.h> +#include <rapidjson/document.h> +#include <vector> + +#include "crypto/hash.h" +#include "cryptonote_basic/account.h" +#include "cryptonote_basic/cryptonote_basic.h" +#include "cryptonote_basic/cryptonote_format_utils.h" +#include "cryptonote_core/cryptonote_tx_utils.h" +#include "serialization/json_object.h" + + +namespace +{ + cryptonote::transaction + make_miner_transaction(cryptonote::account_public_address const& to) + { + cryptonote::transaction tx{}; + if (!cryptonote::construct_miner_tx(0, 0, 5000, 500, 500, to, tx)) + throw std::runtime_error{"transaction construction error"}; + + crypto::hash id{0}; + if (!cryptonote::get_transaction_hash(tx, id)) + throw std::runtime_error{"could not get transaction hash"}; + + return tx; + } + + cryptonote::transaction + make_transaction( + cryptonote::account_keys const& from, + std::vector<cryptonote::transaction> const& sources, + std::vector<cryptonote::account_public_address> const& destinations, + bool rct, + bool bulletproof) + { + std::uint64_t source_amount = 0; + std::vector<cryptonote::tx_source_entry> actual_sources; + for (auto const& source : sources) + { + std::vector<cryptonote::tx_extra_field> extra_fields; + if (!cryptonote::parse_tx_extra(source.extra, extra_fields)) + throw std::runtime_error{"invalid transaction"}; + + cryptonote::tx_extra_pub_key key_field{}; + if (!cryptonote::find_tx_extra_field_by_type(extra_fields, key_field)) + throw std::runtime_error{"invalid transaction"}; + + for (auto const& input : boost::adaptors::index(source.vout)) + { + source_amount += input.value().amount; + auto const& key = boost::get<cryptonote::txout_to_key>(input.value().target); + + actual_sources.push_back( + {{}, 0, key_field.pub_key, {}, std::size_t(input.index()), input.value().amount, rct, rct::identity()} + ); + + for (unsigned ring = 0; ring < 10; ++ring) + actual_sources.back().push_output(input.index(), key.key, input.value().amount); + } + } + + std::vector<cryptonote::tx_destination_entry> to; + for (auto const& destination : destinations) + to.push_back({(source_amount / destinations.size()), destination, false}); + + cryptonote::transaction tx{}; + + crypto::secret_key tx_key{}; + std::vector<crypto::secret_key> extra_keys{}; + + std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses; + subaddresses[from.m_account_address.m_spend_public_key] = {0,0}; + + if (!cryptonote::construct_tx_and_get_tx_key(from, subaddresses, actual_sources, to, boost::none, {}, tx, 0, tx_key, extra_keys, rct, bulletproof)) + throw std::runtime_error{"transaction construction error"}; + + return tx; + } +} // anonymous + +TEST(JsonSerialization, MinerTransaction) +{ + cryptonote::account_base acct; + acct.generate(); + const auto miner_tx = make_miner_transaction(acct.get_keys().m_account_address); + + crypto::hash tx_hash{}; + ASSERT_TRUE(cryptonote::get_transaction_hash(miner_tx, tx_hash)); + + rapidjson::Document doc; + cryptonote::json::toJsonValue(doc, miner_tx, doc); + + cryptonote::transaction miner_tx_copy; + cryptonote::json::fromJsonValue(doc, miner_tx_copy); + + crypto::hash tx_copy_hash{}; + ASSERT_TRUE(cryptonote::get_transaction_hash(miner_tx_copy, tx_copy_hash)); + EXPECT_EQ(tx_hash, tx_copy_hash); + + cryptonote::blobdata tx_bytes{}; + cryptonote::blobdata tx_copy_bytes{}; + + ASSERT_TRUE(cryptonote::t_serializable_object_to_blob(miner_tx, tx_bytes)); + ASSERT_TRUE(cryptonote::t_serializable_object_to_blob(miner_tx_copy, tx_copy_bytes)); + + EXPECT_EQ(tx_bytes, tx_copy_bytes); +} + +TEST(JsonSerialization, RegularTransaction) +{ + cryptonote::account_base acct1; + acct1.generate(); + + cryptonote::account_base acct2; + acct2.generate(); + + const auto miner_tx = make_miner_transaction(acct1.get_keys().m_account_address); + const auto tx = make_transaction( + acct1.get_keys(), {miner_tx}, {acct2.get_keys().m_account_address}, false, false + ); + + crypto::hash tx_hash{}; + ASSERT_TRUE(cryptonote::get_transaction_hash(tx, tx_hash)); + + rapidjson::Document doc; + cryptonote::json::toJsonValue(doc, tx, doc); + + cryptonote::transaction tx_copy; + cryptonote::json::fromJsonValue(doc, tx_copy); + + crypto::hash tx_copy_hash{}; + ASSERT_TRUE(cryptonote::get_transaction_hash(tx_copy, tx_copy_hash)); + EXPECT_EQ(tx_hash, tx_copy_hash); + + cryptonote::blobdata tx_bytes{}; + cryptonote::blobdata tx_copy_bytes{}; + + ASSERT_TRUE(cryptonote::t_serializable_object_to_blob(tx, tx_bytes)); + ASSERT_TRUE(cryptonote::t_serializable_object_to_blob(tx_copy, tx_copy_bytes)); + + EXPECT_EQ(tx_bytes, tx_copy_bytes); +} + +TEST(JsonSerialization, RingctTransaction) +{ + cryptonote::account_base acct1; + acct1.generate(); + + cryptonote::account_base acct2; + acct2.generate(); + + const auto miner_tx = make_miner_transaction(acct1.get_keys().m_account_address); + const auto tx = make_transaction( + acct1.get_keys(), {miner_tx}, {acct2.get_keys().m_account_address}, true, false + ); + + crypto::hash tx_hash{}; + ASSERT_TRUE(cryptonote::get_transaction_hash(tx, tx_hash)); + + rapidjson::Document doc; + cryptonote::json::toJsonValue(doc, tx, doc); + + cryptonote::transaction tx_copy; + cryptonote::json::fromJsonValue(doc, tx_copy); + + crypto::hash tx_copy_hash{}; + ASSERT_TRUE(cryptonote::get_transaction_hash(tx_copy, tx_copy_hash)); + EXPECT_EQ(tx_hash, tx_copy_hash); + + cryptonote::blobdata tx_bytes{}; + cryptonote::blobdata tx_copy_bytes{}; + + ASSERT_TRUE(cryptonote::t_serializable_object_to_blob(tx, tx_bytes)); + ASSERT_TRUE(cryptonote::t_serializable_object_to_blob(tx_copy, tx_copy_bytes)); + + EXPECT_EQ(tx_bytes, tx_copy_bytes); +} + +TEST(JsonSerialization, BulletproofTransaction) +{ + cryptonote::account_base acct1; + acct1.generate(); + + cryptonote::account_base acct2; + acct2.generate(); + + const auto miner_tx = make_miner_transaction(acct1.get_keys().m_account_address); + const auto tx = make_transaction( + acct1.get_keys(), {miner_tx}, {acct2.get_keys().m_account_address}, true, true + ); + + crypto::hash tx_hash{}; + ASSERT_TRUE(cryptonote::get_transaction_hash(tx, tx_hash)); + + rapidjson::Document doc; + cryptonote::json::toJsonValue(doc, tx, doc); + + cryptonote::transaction tx_copy; + cryptonote::json::fromJsonValue(doc, tx_copy); + + crypto::hash tx_copy_hash{}; + ASSERT_TRUE(cryptonote::get_transaction_hash(tx_copy, tx_copy_hash)); + EXPECT_EQ(tx_hash, tx_copy_hash); + + cryptonote::blobdata tx_bytes{}; + cryptonote::blobdata tx_copy_bytes{}; + + ASSERT_TRUE(cryptonote::t_serializable_object_to_blob(tx, tx_bytes)); + ASSERT_TRUE(cryptonote::t_serializable_object_to_blob(tx_copy, tx_copy_bytes)); + + EXPECT_EQ(tx_bytes, tx_copy_bytes); +} + diff --git a/tests/unit_tests/mlocker.cpp b/tests/unit_tests/mlocker.cpp new file mode 100644 index 000000000..6e6048c6c --- /dev/null +++ b/tests/unit_tests/mlocker.cpp @@ -0,0 +1,186 @@ +// Copyright (c) 2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "gtest/gtest.h" + +#include "misc_log_ex.h" +#include "mlocker.h" + +#define BASE(data) (char*)(((uintptr_t)(data.get() + page_size - 1)) / page_size * page_size) + +TEST(mlocker, distinct_1) +{ + const size_t page_size = epee::mlocker::get_page_size(); + ASSERT_TRUE(page_size > 0); + const size_t base_pages = epee::mlocker::get_num_locked_pages(); + const size_t base_objects = epee::mlocker::get_num_locked_objects(); + std::unique_ptr<char[]> data{new char[8 * page_size]}; + epee::mlocker *m0 = new epee::mlocker(BASE(data), 1); + epee::mlocker *m1 = new epee::mlocker(BASE(data) + 2 * page_size, 1); + epee::mlocker *m2 = new epee::mlocker(BASE(data) + 3 * page_size, 1); + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 3); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 3); + delete m0; + delete m1; + delete m2; + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0); +} + +TEST(mlocker, distinct_full_page) +{ + const size_t page_size = epee::mlocker::get_page_size(); + ASSERT_TRUE(page_size > 0); + const size_t base_pages = epee::mlocker::get_num_locked_pages(); + const size_t base_objects = epee::mlocker::get_num_locked_objects(); + std::unique_ptr<char[]> data{new char[8 * page_size]}; + epee::mlocker *m0 = new epee::mlocker(BASE(data), page_size); + epee::mlocker *m1 = new epee::mlocker(BASE(data) + 2 * page_size, page_size); + epee::mlocker *m2 = new epee::mlocker(BASE(data) + 3 * page_size, page_size); + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 3); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 3); + delete m0; + delete m1; + delete m2; + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0); +} + +TEST(mlocker, identical) +{ + const size_t page_size = epee::mlocker::get_page_size(); + ASSERT_TRUE(page_size >= 32); + const size_t base_pages = epee::mlocker::get_num_locked_pages(); + const size_t base_objects = epee::mlocker::get_num_locked_objects(); + std::unique_ptr<char[]> data{new char[8 * page_size]}; + epee::mlocker *m0 = new epee::mlocker(BASE(data) + page_size, 32); + epee::mlocker *m1 = new epee::mlocker(BASE(data) + page_size, 32); + epee::mlocker *m2 = new epee::mlocker(BASE(data) + page_size, 32); + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 3); + delete m1; + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 2); + delete m0; + delete m2; + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0); +} + +TEST(mlocker, overlapping_small) +{ + const size_t page_size = epee::mlocker::get_page_size(); + ASSERT_TRUE(page_size >= 64); + const size_t base_pages = epee::mlocker::get_num_locked_pages(); + const size_t base_objects = epee::mlocker::get_num_locked_objects(); + std::unique_ptr<char[]> data{new char[8 * page_size]}; + epee::mlocker *m0 = new epee::mlocker(BASE(data), 32); + epee::mlocker *m1 = new epee::mlocker(BASE(data) + 16, 32); + epee::mlocker *m2 = new epee::mlocker(BASE(data) + 8, 32); + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 3); + delete m1; + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 2); + delete m2; + delete m0; + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0); +} + +TEST(mlocker, multi_page) +{ + const size_t page_size = epee::mlocker::get_page_size(); + ASSERT_TRUE(page_size > 0); + const size_t base_pages = epee::mlocker::get_num_locked_pages(); + const size_t base_objects = epee::mlocker::get_num_locked_objects(); + std::unique_ptr<char[]> data{new char[8 * page_size]}; + epee::mlocker *m0 = new epee::mlocker(BASE(data) + page_size, page_size * 3); + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 3); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 1); + epee::mlocker *m1 = new epee::mlocker(BASE(data) + page_size * 7, page_size); + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 4); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 2); + delete m0; + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 1); + delete m1; + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0); +} + +TEST(mlocker, cross_page) +{ + const size_t page_size = epee::mlocker::get_page_size(); + ASSERT_TRUE(page_size > 32); + const size_t base_pages = epee::mlocker::get_num_locked_pages(); + const size_t base_objects = epee::mlocker::get_num_locked_objects(); + std::unique_ptr<char[]> data{new char[2 * page_size]}; + epee::mlocker *m0 = new epee::mlocker(BASE(data) + page_size - 1, 2); + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 2); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 1); + delete m0; + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0); +} + +TEST(mlocker, redundant) +{ + const size_t page_size = epee::mlocker::get_page_size(); + const size_t base_pages = epee::mlocker::get_num_locked_pages(); + const size_t base_objects = epee::mlocker::get_num_locked_objects(); + std::unique_ptr<char[]> data{new char[2 * page_size]}; + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0); + epee::mlocker *m0 = new epee::mlocker(BASE(data), 32); + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 1); + epee::mlocker *m1 = new epee::mlocker(BASE(data), 32); + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 2); + delete m1; + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 1); + delete m0; + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0); +} + +TEST(mlocker, mlocked) +{ + const size_t base_pages = epee::mlocker::get_num_locked_pages(); + const size_t base_objects = epee::mlocker::get_num_locked_objects(); + { + struct Foo { uint64_t u; }; + epee::mlocked<Foo> l; + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 1); + } + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0); +} diff --git a/tests/unit_tests/mnemonics.cpp b/tests/unit_tests/mnemonics.cpp index 8fa3192b9..0b74a6b94 100644 --- a/tests/unit_tests/mnemonics.cpp +++ b/tests/unit_tests/mnemonics.cpp @@ -27,6 +27,8 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "gtest/gtest.h" +#include "wipeable_string.h" +#include "mnemonics/language_base.h" #include "mnemonics/electrum-words.h" #include "crypto/crypto.h" #include <stdlib.h> @@ -74,14 +76,16 @@ namespace void test_language(const Language::Base &language) { const std::vector<std::string> &word_list = language.get_word_list(); - std::string seed = "", return_seed = ""; + epee::wipeable_string w_seed = "", w_return_seed = ""; + std::string seed, return_seed; // Generate a random seed without checksum crypto::secret_key randkey; for (size_t ii = 0; ii < sizeof(randkey); ++ii) { randkey.data[ii] = rand(); } - crypto::ElectrumWords::bytes_to_words(randkey, seed, language.get_language_name()); + crypto::ElectrumWords::bytes_to_words(randkey, w_seed, language.get_language_name()); + seed = std::string(w_seed.data(), w_seed.size()); // remove the checksum word const char *space = strrchr(seed.c_str(), ' '); ASSERT_TRUE(space != NULL); @@ -103,7 +107,8 @@ namespace ASSERT_STREQ(language.get_language_name().c_str(), language_name.c_str()); // Convert the secret key back to seed - crypto::ElectrumWords::bytes_to_words(key, return_seed, language.get_language_name()); + crypto::ElectrumWords::bytes_to_words(key, w_return_seed, language.get_language_name()); + return_seed = std::string(w_return_seed.data(), w_return_seed.size()); ASSERT_EQ(true, res); std::cout << "Returned seed:\n"; std::cout << return_seed << std::endl; @@ -126,8 +131,9 @@ namespace std::cout << "Detected language: " << language_name << std::endl; ASSERT_STREQ(language.get_language_name().c_str(), language_name.c_str()); - return_seed = ""; - crypto::ElectrumWords::bytes_to_words(key, return_seed, language.get_language_name()); + w_return_seed = ""; + crypto::ElectrumWords::bytes_to_words(key, w_return_seed, language.get_language_name()); + return_seed = std::string(w_return_seed.data(), w_return_seed.size()); ASSERT_EQ(true, res); std::cout << "Returned seed:\n"; std::cout << return_seed << std::endl; @@ -202,3 +208,17 @@ TEST(mnemonics, language_detection_with_bad_checksum) ASSERT_EQ(true, res); ASSERT_STREQ(language_name.c_str(), "Português"); } + +TEST(mnemonics, utf8prefix) +{ + ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("foo"), 0) == ""); + ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("foo"), 1) == "f"); + ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("foo"), 2) == "fo"); + ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("foo"), 3) == "foo"); + ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("foo"), 4) == "foo"); + ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("æon"), 0) == ""); + ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("æon"), 1) == "æ"); + ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("æon"), 2) == "æo"); + ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("æon"), 3) == "æon"); + ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("æon"), 4) == "æon"); +} diff --git a/tests/unit_tests/multisig.cpp b/tests/unit_tests/multisig.cpp index 922299333..eb453b960 100644 --- a/tests/unit_tests/multisig.cpp +++ b/tests/unit_tests/multisig.cpp @@ -61,10 +61,13 @@ static void make_wallet(unsigned int idx, tools::wallet2 &wallet) try { - wallet.init(""); + wallet.init(false, ""); wallet.set_subaddress_lookahead(1, 1); wallet.generate("", "", spendkey, true, false); ASSERT_TRUE(test_addresses[idx].address == wallet.get_account().get_public_address_str(cryptonote::TESTNET)); + wallet.decrypt_keys(""); + ASSERT_TRUE(test_addresses[idx].spendkey == epee::string_tools::pod_to_hex(wallet.get_account().get_keys().m_spend_secret_key)); + wallet.encrypt_keys(""); } catch (const std::exception &e) { @@ -83,8 +86,12 @@ static void make_M_2_wallet(tools::wallet2 &wallet0, tools::wallet2 &wallet1, un std::vector<crypto::secret_key> sk0(1), sk1(1); std::vector<crypto::public_key> pk0(1), pk1(1); + wallet0.decrypt_keys(""); std::string mi0 = wallet0.get_multisig_info(); + wallet0.encrypt_keys(""); + wallet1.decrypt_keys(""); std::string mi1 = wallet1.get_multisig_info(); + wallet1.encrypt_keys(""); ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi1, sk0[0], pk0[0])); ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi0, sk1[0], pk1[0])); @@ -118,9 +125,15 @@ static void make_M_3_wallet(tools::wallet2 &wallet0, tools::wallet2 &wallet1, to std::vector<crypto::secret_key> sk0(2), sk1(2), sk2(2); std::vector<crypto::public_key> pk0(2), pk1(2), pk2(2); + wallet0.decrypt_keys(""); std::string mi0 = wallet0.get_multisig_info(); + wallet0.encrypt_keys(""); + wallet1.decrypt_keys(""); std::string mi1 = wallet1.get_multisig_info(); + wallet1.encrypt_keys(""); + wallet2.decrypt_keys(""); std::string mi2 = wallet2.get_multisig_info(); + wallet2.encrypt_keys(""); ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi1, sk0[0], pk0[0])); ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi2, sk0[1], pk0[1])); diff --git a/tests/unit_tests/ringdb.cpp b/tests/unit_tests/ringdb.cpp index d50d61b0f..9b842569a 100644 --- a/tests/unit_tests/ringdb.cpp +++ b/tests/unit_tests/ringdb.cpp @@ -39,25 +39,29 @@ #include "crypto/crypto.h" #include "crypto/random.h" #include "crypto/chacha.h" +#include "ringct/rctOps.h" +#include "cryptonote_basic/cryptonote_basic.h" #include "wallet/ringdb.h" static crypto::chacha_key generate_chacha_key() { - uint8_t key[CHACHA_KEY_SIZE]; - crypto::rand(CHACHA_KEY_SIZE, key); crypto::chacha_key chacha_key; - memcpy(&chacha_key, key, CHACHA_KEY_SIZE); + uint64_t password = crypto::rand<uint64_t>(); + crypto::generate_chacha_key(std::string((const char*)&password, sizeof(password)), chacha_key, 1); return chacha_key; } static crypto::key_image generate_key_image() { - return crypto::rand<crypto::key_image>(); + crypto::key_image key_image; + cryptonote::keypair keypair = cryptonote::keypair::generate(hw::get_device("default")); + crypto::generate_key_image(keypair.pub, keypair.sec, key_image); + return key_image; } static crypto::public_key generate_output() { - return crypto::rand<crypto::public_key>(); + return rct::rct2pk(rct::scalarmultBase(rct::skGen())); } @@ -76,13 +80,13 @@ public: private: std::string make_filename() { - boost::filesystem::path path = tools::get_default_data_dir(); - path /= "fake"; + boost::filesystem::path path = + boost::filesystem::temp_directory_path(); #if defined(__MINGW32__) || defined(__MINGW__) - filename = tempnam(path.string().c_str(), "ringdb-test-"); + filename = tempnam(path.string().c_str(), "monero-ringdb-test-"); EXPECT_TRUE(filename != NULL); #else - path /= "ringdb-test-XXXXXX"; + path /= "monero-ringdb-test-XXXXXX"; filename = strdup(path.string().c_str()); EXPECT_TRUE(mkdtemp(filename) != NULL); #endif diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index 07e15e61e..2f7b5aac7 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -671,8 +671,7 @@ TEST(Serialization, serializes_ringct_types) TEST(Serialization, portability_wallet) { const cryptonote::network_type nettype = cryptonote::TESTNET; - const bool restricted = false; - tools::wallet2 w(nettype, restricted); + tools::wallet2 w(nettype); const boost::filesystem::path wallet_file = unit_test::data_dir / "wallet_9svHk1"; string password = "test"; bool r = false; @@ -810,7 +809,7 @@ TEST(Serialization, portability_outputs) if(ciphertext.size() < prefix_size) return {}; crypto::chacha_key key; - crypto::generate_chacha_key(&skey, sizeof(skey), key); + crypto::generate_chacha_key(&skey, sizeof(skey), key, 1); const crypto::chacha_iv &iv = *(const crypto::chacha_iv*)&ciphertext[0]; std::string plaintext; plaintext.resize(ciphertext.size() - prefix_size); diff --git a/tests/unit_tests/wipeable_string.cpp b/tests/unit_tests/wipeable_string.cpp new file mode 100644 index 000000000..5ea1c1729 --- /dev/null +++ b/tests/unit_tests/wipeable_string.cpp @@ -0,0 +1,204 @@ +// Copyright (c) 2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <boost/optional/optional.hpp> +#include <string.h> +#include "gtest/gtest.h" + +#include "misc_log_ex.h" +#include "wipeable_string.h" + +TEST(wipeable_string, ctor) +{ + epee::wipeable_string s0; + ASSERT_EQ(s0.size(), 0); + + epee::wipeable_string s1(std::string("foo")); + ASSERT_EQ(s1.size(), 3); + ASSERT_TRUE(!memcmp(s1.data(), "foo", s1.size())); + + epee::wipeable_string s2(std::string("bar")); + ASSERT_EQ(s2.size(), 3); + ASSERT_TRUE(!memcmp(s2.data(), "bar", s2.size())); + + epee::wipeable_string s3(std::string("quux")); + ASSERT_EQ(s3.size(), 4); + ASSERT_TRUE(!memcmp(s3.data(), "quux", s3.size())); +} + +TEST(wipeable_string, wipe) +{ + epee::wipeable_string s0(std::string("foo")); + ASSERT_EQ(s0.size(), 3); + s0.wipe(); + ASSERT_EQ(s0.size(), 3); + ASSERT_TRUE(!memcmp(s0.data(), "\0\0\0", 3)); +} + +TEST(wipeable_string, clear) +{ + epee::wipeable_string s0(std::string("foo")); + ASSERT_EQ(s0.size(), 3); + s0.clear(); + ASSERT_EQ(s0.size(), 0); +} + +TEST(wipeable_string, push_back) +{ + epee::wipeable_string s0(std::string("fo")); + ASSERT_EQ(s0.size(), 2); + s0.push_back('o'); + ASSERT_EQ(s0.size(), 3); + ASSERT_TRUE(!memcmp(s0.data(), "foo", s0.size())); +} + +TEST(wipeable_string, append_char) +{ + epee::wipeable_string s0(std::string("fo")); + ASSERT_EQ(s0.size(), 2); + s0 += 'o'; + ASSERT_EQ(s0.size(), 3); + ASSERT_TRUE(!memcmp(s0.data(), "foo", s0.size())); +} + +TEST(wipeable_string, append_string) +{ + epee::wipeable_string s0(std::string("foo")); + ASSERT_EQ(s0.size(), 3); + s0 += "bar"; + ASSERT_EQ(s0.size(), 6); + ASSERT_TRUE(!memcmp(s0.data(), "foobar", s0.size())); +} + +TEST(wipeable_string, empty) +{ + epee::wipeable_string s0; + ASSERT_TRUE(s0.empty()); + s0.push_back(' '); + ASSERT_FALSE(s0.empty()); + ASSERT_EQ(s0.pop_back(), ' '); + ASSERT_TRUE(s0.empty()); +} + +TEST(wipeable_string, pop_back) +{ + epee::wipeable_string s = "test"; + ASSERT_EQ(s.size(), 4); + ASSERT_EQ(s.pop_back(), 't'); + ASSERT_EQ(s.size(), 3); + ASSERT_TRUE(!memcmp(s.data(), "tes", s.size())); +} + +TEST(wipeable_string, equal) +{ + epee::wipeable_string s0 = "foo"; + epee::wipeable_string s1 = "bar"; + epee::wipeable_string s0_2 = "foo"; + ASSERT_TRUE(s0 == s0); + ASSERT_TRUE(s0 == s0_2); + ASSERT_TRUE(s1 == s1); + ASSERT_FALSE(s1 == s0); + ASSERT_FALSE(s1 == s0_2); +} + +TEST(wipeable_string, not_equal) +{ + epee::wipeable_string s0 = "foo"; + epee::wipeable_string s1 = "bar"; + epee::wipeable_string s0_2 = "foo"; + ASSERT_FALSE(s0 != s0); + ASSERT_FALSE(s0 != s0_2); + ASSERT_FALSE(s1 != s1); + ASSERT_TRUE(s1 != s0); + ASSERT_TRUE(s1 != s0_2); +} + +static epee::wipeable_string trimmed(const char *s) +{ + epee::wipeable_string str(s); + str.trim(); + return str; +} + +TEST(wipeable_string, trim) +{ + ASSERT_TRUE(trimmed("") == ""); + ASSERT_TRUE(trimmed(" ") == ""); + ASSERT_TRUE(trimmed(" ") == ""); + ASSERT_TRUE(trimmed("a") == "a"); + ASSERT_TRUE(trimmed(" a") == "a"); + ASSERT_TRUE(trimmed(" a") == "a"); + ASSERT_TRUE(trimmed("a ") == "a"); + ASSERT_TRUE(trimmed("a ") == "a"); + ASSERT_TRUE(trimmed(" a ") == "a"); + ASSERT_TRUE(trimmed(" a ") == "a"); + ASSERT_TRUE(trimmed(" ab ") == "ab"); + ASSERT_TRUE(trimmed(" a b ") == "a b"); + ASSERT_TRUE(trimmed(" a b ") == "a b"); +} + +static bool check_split(const char *s, const std::vector<epee::wipeable_string> &v) +{ + epee::wipeable_string str(s); + std::vector<epee::wipeable_string> fields; + str.split(fields); + return v == fields; +} + +TEST(wipeable_string, split) +{ + ASSERT_TRUE(check_split("", {})); + ASSERT_TRUE(check_split("foo", {"foo"})); + ASSERT_TRUE(check_split(" foo ", {"foo"})); + ASSERT_TRUE(check_split("foo bar", {"foo", "bar"})); + ASSERT_TRUE(check_split("foo bar", {"foo", "bar"})); + ASSERT_TRUE(check_split("foo bar baz", {"foo", "bar", "baz"})); + ASSERT_TRUE(check_split(" foo bar baz ", {"foo", "bar", "baz"})); + ASSERT_TRUE(check_split(" foo bar baz", {"foo", "bar", "baz"})); + ASSERT_TRUE(check_split("foo bar baz ", {"foo", "bar", "baz"})); +} + +TEST(wipeable_string, parse_hexstr) +{ + boost::optional<epee::wipeable_string> s; + + ASSERT_EQ(boost::none, epee::wipeable_string("x").parse_hexstr()); + ASSERT_EQ(boost::none, epee::wipeable_string("x0000000000000000").parse_hexstr()); + ASSERT_EQ(boost::none, epee::wipeable_string("0000000000000000x").parse_hexstr()); + ASSERT_EQ(boost::none, epee::wipeable_string("0").parse_hexstr()); + ASSERT_EQ(boost::none, epee::wipeable_string("000").parse_hexstr()); + + ASSERT_TRUE((s = epee::wipeable_string("").parse_hexstr())); + ASSERT_EQ(*s, ""); + ASSERT_TRUE((s = epee::wipeable_string("00").parse_hexstr())); + ASSERT_EQ(*s, epee::wipeable_string("", 1)); + ASSERT_TRUE((s = epee::wipeable_string("41").parse_hexstr())); + ASSERT_EQ(*s, epee::wipeable_string("A")); + ASSERT_TRUE((s = epee::wipeable_string("414243").parse_hexstr())); + ASSERT_EQ(*s, epee::wipeable_string("ABC")); +} |