From 5f146873c58f39632e26c5edbf2f618cacbd76a5 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 18 Feb 2018 10:47:25 +0000 Subject: wallet: add shared ring database This maps key images to rings, so that different forks can reuse the rings by key image. This avoids revealing the real inputs like would happen if two forks spent the same outputs with different rings. This database is meant to be shared with all Monero forks which don't bother making a new chain, putting users' privacy at risk in the process. It is placed in a shared data directory by default ($HOME/.shared-ringdb on UNIX like systems). You may use --shared-ringdb-dir to override this location, and should then do so for all Monero forks for them to share the database. --- src/simplewallet/simplewallet.cpp | 60 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) (limited to 'src/simplewallet/simplewallet.cpp') diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 8bae1e2c9..3363b84ed 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -1294,6 +1294,58 @@ bool simple_wallet::export_raw_multisig(const std::vector &args) return true; } +bool simple_wallet::print_ring(const std::vector &args) +{ + crypto::key_image key_image; + if (args.size() != 1) + { + fail_msg_writer() << tr("usage: print_ring "); + return true; + } + + if (!epee::string_tools::hex_to_pod(args[0], key_image)) + { + fail_msg_writer() << tr("Invalid key image"); + return true; + } + + std::vector ring; + try + { + if (m_wallet->get_ring(m_wallet->get_ring_database(), key_image, ring)) + { + std::stringstream str; + for (const auto &x: ring) + str << x << " "; + success_msg_writer() << tr("Ring size ") << std::to_string(ring.size()) << ": " << str.str(); + } + else + { + fail_msg_writer() << tr("Key image either not spent, or spent with mixin 0"); + } + } + catch (const std::exception &e) + { + fail_msg_writer() << tr("Failed to get key image ring: ") << e.what(); + } + + return true; +} + +bool simple_wallet::save_known_rings(const std::vector &args) +{ + try + { + LOCK_IDLE_SCOPE(); + m_wallet->find_and_save_rings(m_wallet->get_ring_database()); + } + catch (const std::exception &e) + { + fail_msg_writer() << tr("Failed to save known rings: ") << e.what(); + } + return true; +} + bool simple_wallet::set_always_confirm_transfers(const std::vector &args/* = std::vector()*/) { const auto pwd_container = get_and_verify_password(); @@ -1951,6 +2003,14 @@ simple_wallet::simple_wallet() boost::bind(&simple_wallet::export_raw_multisig, this, _1), tr("export_raw_multisig_tx "), tr("Export a signed multisig transaction to a file")); + m_cmd_binder.set_handler("print_ring", + boost::bind(&simple_wallet::print_ring, this, _1), + tr("print_ring "), + tr("Print the ring used to spend a given key image (if the ring size is > 1)")); + m_cmd_binder.set_handler("save_known_rings", + boost::bind(&simple_wallet::save_known_rings, this, _1), + tr("save_known_rings"), + tr("Save known rings to the shared rings database")); m_cmd_binder.set_handler("help", boost::bind(&simple_wallet::help, this, _1), tr("help []"), -- cgit v1.2.3 From 18eaf194897cb8216c9c035871911e6ba099a179 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 19 Feb 2018 10:23:23 +0000 Subject: wallet: key reuse mitigation options If a pre-fork output is spent on both Monero and attack chain, any post-fork output can be deduced to be a fake output, thereby decreasing the effective ring size. The segregate-per-fork-outputs option, on by default, allows selecting only pre-fork outputs in this case, so that the same ring can be used when spending it on the other side, which does not decrease the effective ring size. This is intended to be SET when intending to spend Monero on the attack fork, and to be UNSET if not intending to spend Monero on the attack fork (since it leaks the fact that the output being spent is pre-fork). If the user is not certain yet whether they will spend pre-fork outputs on a key reusing fork, the key-reuse-mitigation2 option should be SET instead. If you use this option and intend to spend Monero on both forks, then spend real Monero first. --- src/simplewallet/simplewallet.cpp | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) (limited to 'src/simplewallet/simplewallet.cpp') diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 3363b84ed..698ecced8 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -1690,6 +1690,32 @@ bool simple_wallet::set_auto_low_priority(const std::vector &args/* return true; } +bool simple_wallet::set_segregate_pre_fork_outputs(const std::vector &args/* = std::vector()*/) +{ + const auto pwd_container = get_and_verify_password(); + if (pwd_container) + { + parse_bool_and_use(args[1], [&](bool r) { + m_wallet->segregate_pre_fork_outputs(r); + m_wallet->rewrite(m_wallet_file, pwd_container->password()); + }); + } + return true; +} + +bool simple_wallet::set_key_reuse_mitigation2(const std::vector &args/* = std::vector()*/) +{ + const auto pwd_container = get_and_verify_password(); + if (pwd_container) + { + parse_bool_and_use(args[1], [&](bool r) { + m_wallet->key_reuse_mitigation2(r); + m_wallet->rewrite(m_wallet_file, pwd_container->password()); + }); + } + return true; +} + bool simple_wallet::help(const std::vector &args/* = std::vector()*/) { if(args.empty()) @@ -1864,7 +1890,11 @@ simple_wallet::simple_wallet() "refresh-from-block-height [n]\n " " Set the height before which to ignore blocks.\n " "auto-low-priority <1|0>\n " - " Whether to automatically use the low priority fee level when it's safe to do so.")); + " Whether to automatically use the low priority fee level when it's safe to do so.\n " + "segregate-pre-fork-outputs <1|0>\n " + " Set this if you intend to spend outputs on both Monero AND a key reusing fork.\n " + "key-reuse-mitigation2 <1|0>\n " + " Set this if you are not sure whether you will spend on a key reusing Monero fork later.")); m_cmd_binder.set_handler("encrypted_seed", boost::bind(&simple_wallet::encrypted_seed, this, _1), tr("Display the encrypted Electrum-style mnemonic seed.")); @@ -2040,6 +2070,8 @@ bool simple_wallet::set_variable(const std::vector &args) success_msg_writer() << "confirm-export-overwrite = " << m_wallet->confirm_export_overwrite(); success_msg_writer() << "refresh-from-block-height = " << m_wallet->get_refresh_from_block_height(); success_msg_writer() << "auto-low-priority = " << m_wallet->auto_low_priority(); + success_msg_writer() << "segregate-pre-fork-outputs = " << m_wallet->segregate_pre_fork_outputs(); + success_msg_writer() << "key-reuse-mitigation2 = " << m_wallet->key_reuse_mitigation2(); return true; } else @@ -2090,6 +2122,8 @@ bool simple_wallet::set_variable(const std::vector &args) CHECK_SIMPLE_VARIABLE("confirm-export-overwrite", set_confirm_export_overwrite, tr("0 or 1")); CHECK_SIMPLE_VARIABLE("refresh-from-block-height", set_refresh_from_block_height, tr("block height")); CHECK_SIMPLE_VARIABLE("auto-low-priority", set_auto_low_priority, tr("0 or 1")); + CHECK_SIMPLE_VARIABLE("segregate-pre-fork-outputs", set_segregate_pre_fork_outputs, tr("0 or 1")); + CHECK_SIMPLE_VARIABLE("key-reuse-mitigation2", set_key_reuse_mitigation2, tr("0 or 1")); } fail_msg_writer() << tr("set: unrecognized argument(s)"); return true; -- cgit v1.2.3 From d29ea0455a34c2d08c681dacdf809e7f7317d2b3 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 25 Feb 2018 19:20:07 +0000 Subject: wallet: add an output blackball list to avoid using those in rings --- src/simplewallet/simplewallet.cpp | 141 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) (limited to 'src/simplewallet/simplewallet.cpp') diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 698ecced8..315d91aa6 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -1332,6 +1332,135 @@ bool simple_wallet::print_ring(const std::vector &args) return true; } +bool simple_wallet::blackball(const std::vector &args) +{ + crypto::public_key output; + if (args.size() == 0) + { + fail_msg_writer() << tr("usage: blackball | [add]"); + return true; + } + + try + { + if (epee::string_tools::hex_to_pod(args[0], output)) + { + m_wallet->blackball_output(output); + } + else if (epee::file_io_utils::is_file_exist(args[0])) + { + std::vector outputs; + char str[65]; + + std::unique_ptr f(fopen(args[0].c_str(), "r")); + if (f) + { + while (!feof(f.get())) + { + if (!fgets(str, sizeof(str), f.get())) + break; + const size_t len = strlen(str); + if (len > 0 && str[len - 1] == '\n') + str[len - 1] = 0; + if (!str[0]) + continue; + outputs.push_back(crypto::public_key()); + if (!epee::string_tools::hex_to_pod(str, outputs.back())) + { + fail_msg_writer() << tr("Invalid public key: ") << str; + return true; + } + } + f.reset(); + bool add = false; + if (args.size() > 1) + { + if (args[1] != "add") + { + fail_msg_writer() << tr("Bad argument: ") + args[1] + ": " + tr("should be \"add\""); + return true; + } + add = true; + } + m_wallet->set_blackballed_outputs(outputs, add); + } + else + { + fail_msg_writer() << tr("Failed to open file"); + return true; + } + } + else + { + fail_msg_writer() << tr("Invalid public key, and file doesn't exist"); + return true; + } + } + catch (const std::exception &e) + { + fail_msg_writer() << tr("Failed to blackball output: ") << e.what(); + } + + return true; +} + +bool simple_wallet::unblackball(const std::vector &args) +{ + crypto::public_key output; + if (args.size() != 1) + { + fail_msg_writer() << tr("usage: unblackball "); + return true; + } + + if (!epee::string_tools::hex_to_pod(args[0], output)) + { + fail_msg_writer() << tr("Invalid public key"); + return true; + } + + try + { + m_wallet->unblackball_output(output); + } + catch (const std::exception &e) + { + fail_msg_writer() << tr("Failed to unblackball output: ") << e.what(); + } + + return true; +} + +bool simple_wallet::blackballed(const std::vector &args) +{ + crypto::public_key output; + if (args.size() != 1) + { + fail_msg_writer() << tr("usage: blackballed "); + return true; + } + + if (!epee::string_tools::hex_to_pod(args[0], output)) + { + fail_msg_writer() << tr("Invalid public key"); + return true; + } + + try + { + if (m_wallet->is_output_blackballed(output)) + message_writer() << tr("Blackballed: ") << output; + else + message_writer() << tr("not blackballed: ") << output; + } + catch (const std::exception &e) + { + fail_msg_writer() << tr("Failed to unblackball output: ") << e.what(); + } + + return true; +} + bool simple_wallet::save_known_rings(const std::vector &args) { try @@ -2041,6 +2170,18 @@ simple_wallet::simple_wallet() boost::bind(&simple_wallet::save_known_rings, this, _1), tr("save_known_rings"), tr("Save known rings to the shared rings database")); + m_cmd_binder.set_handler("blackball", + boost::bind(&simple_wallet::blackball, this, _1), + tr("blackball | [add]"), + tr("Blackball output(s) so they never get selected as fake outputs in a ring")); + m_cmd_binder.set_handler("unblackball", + boost::bind(&simple_wallet::unblackball, this, _1), + tr("unblackball "), + tr("Unblackballs an output so it may get selected as a fake output in a ring")); + m_cmd_binder.set_handler("blackballed", + boost::bind(&simple_wallet::blackballed, this, _1), + tr("blackballed "), + tr("Checks whether an output is blackballed")); m_cmd_binder.set_handler("help", boost::bind(&simple_wallet::help, this, _1), tr("help []"), -- cgit v1.2.3 From db10dd6d8329de92e212247a2eb101c773d4c3cd Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 27 Feb 2018 08:30:59 +0000 Subject: wallet: make ringdb an object with database state --- src/simplewallet/simplewallet.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/simplewallet/simplewallet.cpp') diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 315d91aa6..89edee3fe 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -1312,7 +1312,7 @@ bool simple_wallet::print_ring(const std::vector &args) std::vector ring; try { - if (m_wallet->get_ring(m_wallet->get_ring_database(), key_image, ring)) + if (m_wallet->get_ring(key_image, ring)) { std::stringstream str; for (const auto &x: ring) @@ -1466,7 +1466,7 @@ bool simple_wallet::save_known_rings(const std::vector &args) try { LOCK_IDLE_SCOPE(); - m_wallet->find_and_save_rings(m_wallet->get_ring_database()); + m_wallet->find_and_save_rings(); } catch (const std::exception &e) { -- cgit v1.2.3 From b09e5181cc7c24fc6cf13b4a43044833bd2763fa Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 28 Feb 2018 18:26:06 +0000 Subject: wallet: add a set_ring command This is so one can set rings for spent key images in case the attackers don't merge the ring matching patch set. --- src/simplewallet/simplewallet.cpp | 81 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) (limited to 'src/simplewallet/simplewallet.cpp') diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 89edee3fe..61a234317 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -1317,7 +1317,7 @@ bool simple_wallet::print_ring(const std::vector &args) std::stringstream str; for (const auto &x: ring) str << x << " "; - success_msg_writer() << tr("Ring size ") << std::to_string(ring.size()) << ": " << str.str(); + success_msg_writer() << tr("Ring size ") << std::to_string(ring.size()) << ": " << str.str() << tr(" (absolute)"); } else { @@ -1332,6 +1332,81 @@ bool simple_wallet::print_ring(const std::vector &args) return true; } +bool simple_wallet::set_ring(const std::vector &args) +{ + crypto::key_image key_image; + if (args.size() < 3) + { + fail_msg_writer() << tr("usage: set_ring absolute|relative [...]"); + return true; + } + + if (!epee::string_tools::hex_to_pod(args[0], key_image)) + { + fail_msg_writer() << tr("Invalid key image"); + return true; + } + + bool relative; + if (args[1] == "absolute") + { + relative = false; + } + else if (args[1] == "relative") + { + relative = true; + } + else + { + fail_msg_writer() << tr("Missing absolute or relative keyword"); + return true; + } + + std::vector ring; + for (size_t n = 2; n < args.size(); ++n) + { + ring.resize(ring.size() + 1); + if (!string_tools::get_xtype_from_string(ring.back(), args[n])) + { + fail_msg_writer() << tr("invalid index: must be a strictly positive unsigned integer"); + return true; + } + if (relative) + { + if (ring.size() > 1 && !ring.back()) + { + fail_msg_writer() << tr("invalid index: must be a strictly positive unsigned integer"); + return true; + } + uint64_t sum = 0; + for (uint64_t out: ring) + { + if (out > std::numeric_limits::max() - sum) + { + fail_msg_writer() << tr("invalid index: indices wrap"); + return true; + } + sum += out; + } + } + else + { + if (ring.size() > 1 && ring[ring.size() - 2] >= ring[ring.size() - 1]) + { + fail_msg_writer() << tr("invalid index: indices should be in strictly ascending order"); + return true; + } + } + } + if (!m_wallet->set_ring(key_image, ring, relative)) + { + fail_msg_writer() << tr("failed to set ring"); + return true; + } + + return true; +} + bool simple_wallet::blackball(const std::vector &args) { crypto::public_key output; @@ -2166,6 +2241,10 @@ simple_wallet::simple_wallet() boost::bind(&simple_wallet::print_ring, this, _1), tr("print_ring "), tr("Print the ring used to spend a given key image (if the ring size is > 1)")); + m_cmd_binder.set_handler("set_ring", + boost::bind(&simple_wallet::set_ring, this, _1), + tr("set_ring absolute|relative [...]"), + tr("Set the ring used for a given key image, so it can be reused in a fork")); m_cmd_binder.set_handler("save_known_rings", boost::bind(&simple_wallet::save_known_rings, this, _1), tr("save_known_rings"), -- cgit v1.2.3 From eac3a11ed330faefa01685e98cf91e9ac1e31135 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 14 Mar 2018 19:13:55 +0000 Subject: wallet: more user friendly print_ring It can now take a txid (to display rings for all its inputs), and will print rings in a format that set_ring understands --- src/simplewallet/simplewallet.cpp | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) (limited to 'src/simplewallet/simplewallet.cpp') diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 61a234317..3c257979e 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -1297,9 +1297,10 @@ bool simple_wallet::export_raw_multisig(const std::vector &args) bool simple_wallet::print_ring(const std::vector &args) { crypto::key_image key_image; + crypto::hash txid; if (args.size() != 1) { - fail_msg_writer() << tr("usage: print_ring "); + fail_msg_writer() << tr("usage: print_ring "); return true; } @@ -1308,20 +1309,32 @@ bool simple_wallet::print_ring(const std::vector &args) fail_msg_writer() << tr("Invalid key image"); return true; } + // this one will always work, they're all 32 byte hex + if (!epee::string_tools::hex_to_pod(args[0], txid)) + { + fail_msg_writer() << tr("Invalid txid"); + return true; + } std::vector ring; + std::vector>> rings; try { if (m_wallet->get_ring(key_image, ring)) + rings.push_back({key_image, ring}); + else if (!m_wallet->get_rings(txid, rings)) { - std::stringstream str; - for (const auto &x: ring) - str << x << " "; - success_msg_writer() << tr("Ring size ") << std::to_string(ring.size()) << ": " << str.str() << tr(" (absolute)"); + fail_msg_writer() << tr("Key image either not spent, or spent with mixin 0"); + return true; } - else + + for (const auto &ring: rings) { - fail_msg_writer() << tr("Key image either not spent, or spent with mixin 0"); + std::stringstream str; + for (const auto &x: ring.second) + str << x<< " "; + // do NOT translate this "absolute" below, the lin can be used as input to set_ring + success_msg_writer() << epee::string_tools::pod_to_hex(ring.first) << " absolute " << str.str(); } } catch (const std::exception &e) @@ -2239,8 +2252,8 @@ simple_wallet::simple_wallet() tr("Export a signed multisig transaction to a file")); m_cmd_binder.set_handler("print_ring", boost::bind(&simple_wallet::print_ring, this, _1), - tr("print_ring "), - tr("Print the ring used to spend a given key image (if the ring size is > 1)")); + tr("print_ring | "), + tr("Print the ring(s) used to spend a given key image or transaction (if the ring size is > 1)")); m_cmd_binder.set_handler("set_ring", boost::bind(&simple_wallet::set_ring, this, _1), tr("set_ring absolute|relative [...]"), -- cgit v1.2.3